Squashed commit of the following:

Updated the supported python versions list.
    Fixed regression in displaying xposts. #173.
    Fixing a few style things.
    Added a more robust test for the tornado handler.
    Trying without pytest-cov
    Updated travis for coverage.
    Remove python 3.2 support because no unicode literals, following what praw supports.
    "Side effect is not iterable."
    Added requirements for travis.
    Renamed travis file correctly.
    Adding test configurations, got tox working.
    Adding vcr cassettes to the repo.
    Renamed requirements files.
    Split up tests and cleaned up test names.
    Tests done, still one failure.
    Treat cassettes as binary to prevent bad merging.
    Fixed a few broken tests.
    Added a timeout to notifications.
    Prepping subreddit page.
    Finished submission page tests.
    Working on submission tests.
    Fixed vcr matching on urls with params, started submission tests.
    Log cleanup.
    Still trying to fix a broken test.
    -Fixed a few pytest bugs and tweaked logging.
    Still working on subscription tests.
    Finished page tests, on to subscription page.
    Finished content tests and starting page tests.
    Added the test refresh-token file to gitignore.
    Moved functional test file out of the repository.
    Continuing work on subreddit content tests.
    Tests now match module names, cassettes are split into individual tests for faster loading.
    Linter fixes.
    Cleanup.
    Added support for nested loaders.
    Added pytest options, starting subreddit content tests.
    Back on track with loader, continuing content tests.
    Finishing submission content tests and discovered snag with loader exception handling.
    VCR up and running, continuing to implement content tests.
    Playing around with vcr.py
    Moved helper functions into terminal and new objects.py
    Fixed a few broken tests.
    Working on navigator tests.
    Reorganizing some things.
    Mocked webbrowser._tryorder for terminal test.
    Completed oauth tests.
    Progress on the oauth tests.
    Working on adding fake tornado request.
    Starting on OAuth tool tests.
    Finished curses helpers tests.
    Still working on curses helpers tests.
    Almost finished with tests on curses helpers.
    Adding tests and working on mocking stdscr.
    Starting to add tests for curses functions.
    Merge branch 'future_work' of https://github.com/michael-lazar/rtv into future_work
    Refactoring controller, still in progress.
    Renamed auth handler.
    Rename CursesHelper to CursesBase.
    Added temporary file with a possible template for func testing.
    Mixup between basename and dirname.
    Merge branch 'future_work' of https://github.com/michael-lazar/rtv into future_work
    py3 compatability for mock.
    Beginning to refactor the curses session.
    Started adding tests, improved unicode handling in the config.
    Cleanup, fixed a few typos.
    Major refactor, almost done!.
    Started a config class.
    Merge branch 'master' into future_work
    The editor now handles unicode characters in all situations.
    Fixed a few typos from previous commits.
    __main__.py formatting.
    Cleaned up history logic and moved to the config file.
This commit is contained in:
Michael Lazar
2015-12-02 22:37:50 -08:00
parent b91bb86e36
commit a7b789bfd9
70 changed files with 42141 additions and 1560 deletions

3
.coveragerc Normal file
View File

@@ -0,0 +1,3 @@
[run]
source = rtv
omit = *__main__.py

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
tests/cassettes/* binary

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@
build
dist
rtv.egg-info
tests/refresh-token

15
.travis.yml Normal file
View File

@@ -0,0 +1,15 @@
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
before_install:
- pip install coveralls pytest coverage mock
- pip install git+https://github.com/kevin1024/vcrpy.git
install:
- pip install .
script:
- coverage run -m py.test -v
after_success:
- coveralls

View File

@@ -1,6 +0,0 @@
tornado
praw>=3.3.0
six
requests
kitchen
futures

View File

@@ -1,5 +0,0 @@
tornado
praw>=3.3.0
six
requests
kitchen

View File

@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from .__version__ import __version__
__title__ = 'Reddit Terminal Viewer'

View File

@@ -1,22 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
import locale
import logging
import praw
import praw.errors
import tornado
from requests import exceptions
from . import config
from .exceptions import RTVError
from .curses_helpers import curses_session, LoadScreen
from .submission import SubmissionPage
from . import docs
from .config import Config
from .oauth import OAuthHelper
from .terminal import Terminal
from .objects import curses_session
from .subreddit import SubredditPage
from .docs import AGENT
from .oauth import OAuthTool
from .__version__ import __version__
__all__ = []
_logger = logging.getLogger(__name__)
# Pycharm debugging note:
@@ -35,55 +34,64 @@ def main():
locale.setlocale(locale.LC_ALL, '')
# Set the terminal title
# TODO: Need to clear the title when the program exits
title = 'rtv {0}'.format(__version__)
sys.stdout.write("\x1b]2;{0}\x07".format(title))
sys.stdout.write('\x1b]2;{0}\x07'.format(title))
# Fill in empty arguments with config file values. Parameters explicitly
# typed on the command line will take priority over config file params.
parser = config.build_parser()
args = parser.parse_args()
# Attempt to load from the config file first, and then overwrite with any
# provided command line arguments.
config = Config()
config.from_file()
config.from_args()
local_config = config.load_config()
for key, val in local_config.items():
if getattr(args, key, None) is None:
setattr(args, key, val)
# Load the browsing history from previous sessions
config.load_history()
if args.ascii:
config.unicode = False
if not args.persistent:
config.persistent = False
if args.clear_auth:
config.clear_refresh_token()
# Load any previously saved auth session token
config.load_refresh_token()
if config['clear_auth']:
config.delete_refresh_token()
if args.log:
logging.basicConfig(level=logging.DEBUG, filename=args.log)
if config['log']:
logging.basicConfig(level=logging.DEBUG, filename=config['log'])
else:
# Add an empty handler so the logger doesn't complain
logging.root.addHandler(logging.NullHandler())
# Construct the reddit user agent
user_agent = docs.AGENT.format(version=__version__)
try:
print('Connecting...')
reddit = praw.Reddit(user_agent=AGENT.format(version=__version__))
reddit.config.decode_html_entities = False
with curses_session() as stdscr:
oauth = OAuthTool(reddit, stdscr, LoadScreen(stdscr))
if oauth.refresh_token:
term = Terminal(stdscr, config['ascii'])
with term.loader(catch_exception=False):
reddit = praw.Reddit(
user_agent=user_agent,
decode_html_entities=False,
disable_update_check=True)
# Authorize on launch if the refresh token is present
oauth = OAuthHelper(reddit, term, config)
if config.refresh_token:
oauth.authorize()
if args.link:
page = SubmissionPage(stdscr, reddit, oauth, url=args.link)
with term.loader():
page = SubredditPage(
reddit, term, config, oauth,
name=config['subreddit'], url=config['link'])
if term.loader.exception:
return
page.loop()
subreddit = args.subreddit or 'front'
page = SubredditPage(stdscr, reddit, oauth, subreddit)
page.loop()
except (exceptions.RequestException, praw.errors.PRAWException,
RTVError) as e:
except Exception as e:
_logger.exception(e)
print('{}: {}'.format(type(e).__name__, e))
raise
except KeyboardInterrupt:
pass
finally:
# Try to save the browsing history
config.save_history()
# Ensure sockets are closed to prevent a ResourceWarning
if 'reddit' in locals():
reddit.handler.http.close()
# Explicitly close file descriptors opened by Tornado's IOLoop
tornado.ioloop.IOLoop.current().close(all_fds=True)

View File

@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
__version__ = '1.6.1'

View File

@@ -1,38 +1,30 @@
"""
Global configuration settings
"""
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import codecs
import argparse
from six.moves import configparser
from . import docs, __version__
HOME = os.path.expanduser('~')
PACKAGE = os.path.dirname(__file__)
XDG_HOME = os.getenv('XDG_CONFIG_HOME', os.path.join(HOME, '.config'))
CONFIG = os.path.join(XDG_HOME, 'rtv', 'rtv.cfg')
TOKEN = os.path.join(XDG_HOME, 'rtv', 'refresh-token')
unicode = True
persistent = True
# https://github.com/reddit/reddit/wiki/OAuth2
# Client ID is of type "installed app" and the secret should be left empty
oauth_client_id = 'E2oEtRQfdfAfNQ'
oauth_client_secret = 'praw_gapfill'
oauth_redirect_uri = 'http://127.0.0.1:65000/'
oauth_redirect_port = 65000
oauth_scope = ['edit', 'history', 'identity', 'mysubreddits',
'privatemessages', 'read', 'report', 'save', 'submit',
'subscribe', 'vote']
HISTORY = os.path.join(XDG_HOME, 'rtv', 'history.log')
TEMPLATE = os.path.join(PACKAGE, 'templates')
def build_parser():
parser = argparse.ArgumentParser(
prog='rtv', description=docs.SUMMARY, epilog=docs.CONTROLS+docs.HELP,
prog='rtv', description=docs.SUMMARY,
epilog=docs.CONTROLS+docs.HELP,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'-V', '--version', action='version', version='rtv '+__version__,
)
'-V', '--version', action='version', version='rtv '+__version__)
parser.add_argument(
'-s', dest='subreddit',
help='name of the subreddit that will be opened on start')
@@ -40,28 +32,108 @@ def build_parser():
'-l', dest='link',
help='full URL of a submission that will be opened on start')
parser.add_argument(
'--ascii', action='store_true',
'--ascii', action='store_const', const=True,
help='enable ascii-only mode')
parser.add_argument(
'--log', metavar='FILE', action='store',
help='log HTTP requests to a file')
parser.add_argument(
'--non-persistent', dest='persistent', action='store_false',
'--non-persistent', dest='persistent', action='store_const',
const=False,
help='Forget all authenticated users when the program exits')
parser.add_argument(
'--clear-auth', dest='clear_auth', action='store_true',
'--clear-auth', dest='clear_auth', action='store_const', const=True,
help='Remove any saved OAuth tokens before starting')
return parser
def load_config():
class OrderedSet(object):
"""
Attempt to load settings from the local config file.
A simple implementation of an ordered set. A set is used to check
for membership, and a list is used to maintain ordering.
"""
def __init__(self, elements=None):
elements = elements or []
self._set = set(elements)
self._list = elements
def __contains__(self, item):
return item in self._set
def __len__(self):
return len(self._list)
def __getitem__(self, item):
return self._list[item]
def add(self, item):
self._set.add(item)
self._list.append(item)
class Config(object):
DEFAULT = {
'ascii': False,
'persistent': True,
'clear_auth': False,
'log': None,
'link': None,
'subreddit': 'front',
'history_size': 200,
# https://github.com/reddit/reddit/wiki/OAuth2
# Client ID is of type "installed app" and the secret should be empty
'oauth_client_id': 'E2oEtRQfdfAfNQ',
'oauth_client_secret': 'praw_gapfill',
'oauth_redirect_uri': 'http://127.0.0.1:65000/',
'oauth_redirect_port': 65000,
'oauth_scope': [
'edit', 'history', 'identity', 'mysubreddits', 'privatemessages',
'read', 'report', 'save', 'submit', 'subscribe', 'vote'],
'template_path': TEMPLATE,
}
def __init__(self,
config_file=CONFIG,
history_file=HISTORY,
token_file=TOKEN,
**kwargs):
self.config_file = config_file
self.history_file = history_file
self.token_file = token_file
self.config = kwargs
# `refresh_token` and `history` are saved/loaded at separate locations,
# so they are treated differently from the rest of the config options.
self.refresh_token = None
self.history = OrderedSet()
def __getitem__(self, item):
return self.config.get(item, self.DEFAULT.get(item))
def __setitem__(self, key, value):
self.config[key] = value
def __delitem__(self, key):
self.config.pop(key, None)
def update(self, **kwargs):
self.config.update(kwargs)
def from_args(self):
parser = build_parser()
args = vars(parser.parse_args())
# Filter out argument values that weren't supplied
args = {key: val for key, val in args.items() if val is not None}
self.update(**args)
def from_file(self):
config = configparser.ConfigParser()
if os.path.exists(CONFIG):
config.read(CONFIG)
if os.path.exists(self.config_file):
with codecs.open(self.config_file, encoding='utf-8') as fp:
config.readfp(fp)
config_dict = {}
if config.has_section('rtv'):
@@ -75,22 +147,48 @@ def load_config():
if 'persistent' in config_dict:
config_dict['persistent'] = config.getboolean('rtv', 'persistent')
return config_dict
self.update(**config_dict)
def load_refresh_token(filename=TOKEN):
if os.path.exists(filename):
with open(filename) as fp:
return fp.read().strip()
def load_refresh_token(self):
if os.path.exists(self.token_file):
with open(self.token_file) as fp:
self.refresh_token = fp.read().strip()
else:
return None
self.refresh_token = None
def save_refresh_token(self):
self._ensure_filepath(self.token_file)
with open(self.token_file, 'w+') as fp:
fp.write(self.refresh_token)
def save_refresh_token(token, filename=TOKEN):
with open(filename, 'w+') as fp:
fp.write(token)
def delete_refresh_token(self):
if os.path.exists(self.token_file):
os.remove(self.token_file)
self.refresh_token = None
def load_history(self):
if os.path.exists(self.history_file):
with codecs.open(self.history_file, encoding='utf-8') as fp:
self.history = OrderedSet([line.strip() for line in fp])
else:
self.history = OrderedSet()
def clear_refresh_token(filename=TOKEN):
if os.path.exists(filename):
os.remove(filename)
def save_history(self):
self._ensure_filepath(self.history_file)
with codecs.open(self.history_file, 'w+', encoding='utf-8') as fp:
fp.writelines('\n'.join(self.history[-self['history_size']:]))
def delete_history(self):
if os.path.exists(self.history_file):
os.remove(self.history_file)
self.history = OrderedSet()
@staticmethod
def _ensure_filepath(filename):
"""
Ensure that the directory exists before trying to write to the file.
"""
filepath = os.path.dirname(filename)
if not os.path.exists(filepath):
os.makedirs(filepath)

View File

@@ -1,28 +1,27 @@
import logging
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import praw
import requests
import re
from datetime import datetime
from .exceptions import (SubmissionError, SubredditError, SubscriptionError,
AccountError)
from .helpers import humanize_timestamp, wrap_text, strip_subreddit_url
import six
import praw
from kitchen.text.display import wrap
__all__ = ['SubredditContent', 'SubmissionContent', 'SubscriptionContent']
_logger = logging.getLogger(__name__)
from . import exceptions
class BaseContent(object):
class Content(object):
def get(self, index, n_cols):
raise NotImplementedError
def iterate(self, index, step, n_cols):
def iterate(self, index, step, n_cols=70):
while True:
if step < 0 and index < 0:
# Hack to prevent displaying negative indices if iterating in
# the negative direction.
# Hack to prevent displaying a submission's post if iterating
# comments in the negative direction
break
try:
yield self.get(index, n_cols=n_cols)
@@ -63,8 +62,8 @@ class BaseContent(object):
retval.append(item)
return retval
@staticmethod
def strip_praw_comment(comment):
@classmethod
def strip_praw_comment(cls, comment):
"""
Parse through a submission comment and return a dict with data ready to
be displayed through the terminal.
@@ -89,7 +88,7 @@ class BaseContent(object):
data['type'] = 'Comment'
data['body'] = comment.body
data['created'] = humanize_timestamp(comment.created_utc)
data['created'] = cls.humanize_timestamp(comment.created_utc)
data['score'] = '{} pts'.format(comment.score)
data['author'] = name
data['is_author'] = (name == sub_name)
@@ -100,8 +99,8 @@ class BaseContent(object):
return data
@staticmethod
def strip_praw_submission(sub):
@classmethod
def strip_praw_submission(cls, sub):
"""
Parse through a submission and return a dict with data ready to be
displayed through the terminal.
@@ -114,7 +113,7 @@ class BaseContent(object):
"""
reddit_link = re.compile(
"https?://(www\.)?(np\.)?redd(it\.com|\.it)/r/.*")
'https?://(www\.)?(np\.)?redd(it\.com|\.it)/r/.*')
author = getattr(sub, 'author', '[deleted]')
name = getattr(author, 'name', '[deleted]')
flair = getattr(sub, 'link_flair_text', '')
@@ -124,12 +123,12 @@ class BaseContent(object):
data['type'] = 'Submission'
data['title'] = sub.title
data['text'] = sub.selftext
data['created'] = humanize_timestamp(sub.created_utc)
data['created'] = cls.humanize_timestamp(sub.created_utc)
data['comments'] = '{} comments'.format(sub.num_comments)
data['score'] = '{} pts'.format(sub.score)
data['author'] = name
data['permalink'] = sub.permalink
data['subreddit'] = str(sub.subreddit)
data['subreddit'] = six.text_type(sub.subreddit)
data['flair'] = flair
data['url_full'] = sub.url
data['likes'] = sub.likes
@@ -146,7 +145,9 @@ class BaseContent(object):
data['url'] = 'self.{}'.format(data['subreddit'])
elif reddit_link.match(url_full):
data['url_type'] = 'x-post'
data['url'] = 'self.{}'.format(strip_subreddit_url(url_full)[3:])
# Strip the subreddit name from the permalink to avoid having
# submission.subreddit.url make a separate API call
data['url'] = 'self.{}'.format(url_full.split('/')[4])
else:
data['url_type'] = 'external'
data['url'] = url_full
@@ -165,11 +166,51 @@ class BaseContent(object):
data['type'] = 'Subscription'
data['name'] = "/r/" + subscription.display_name
data['title'] = subscription.title
return data
@staticmethod
def humanize_timestamp(utc_timestamp, verbose=False):
"""
Convert a utc timestamp into a human readable relative-time.
"""
class SubmissionContent(BaseContent):
timedelta = datetime.utcnow() - datetime.utcfromtimestamp(utc_timestamp)
seconds = int(timedelta.total_seconds())
if seconds < 60:
return 'moments ago' if verbose else '0min'
minutes = seconds // 60
if minutes < 60:
return '%d minutes ago' % minutes if verbose else '%dmin' % minutes
hours = minutes // 60
if hours < 24:
return '%d hours ago' % hours if verbose else '%dhr' % hours
days = hours // 24
if days < 30:
return '%d days ago' % days if verbose else '%dday' % days
months = days // 30.4
if months < 12:
return '%d months ago' % months if verbose else '%dmonth' % months
years = months // 12
return '%d years ago' % years if verbose else '%dyr' % years
@staticmethod
def wrap_text(text, width):
"""
Wrap text paragraphs to the given character width while preserving
newlines.
"""
out = []
for paragraph in text.splitlines():
# Wrap returns an empty list when paragraph is a newline. In order
# to preserve newlines we substitute a list containing an empty
# string.
lines = wrap(paragraph, width=width) or ['']
out.extend(lines)
return out
class SubmissionContent(Content):
"""
Grab a submission from PRAW and lazily store comments to an internal
list for repeat access.
@@ -194,13 +235,8 @@ class SubmissionContent(BaseContent):
def from_url(cls, reddit, url, loader, indent_size=2, max_indent_level=8,
order=None):
try:
with loader():
url = url.replace('http:', 'https:')
submission = reddit.get_submission(url, comment_sort=order)
except (praw.errors.APIException, praw.errors.NotFound):
raise SubmissionError('Could not load %s' % url)
return cls(submission, loader, indent_size, max_indent_level, order)
def get(self, index, n_cols=70):
@@ -214,8 +250,8 @@ class SubmissionContent(BaseContent):
elif index == -1:
data = self._submission_data
data['split_title'] = wrap_text(data['title'], width=n_cols-2)
data['split_text'] = wrap_text(data['text'], width=n_cols-2)
data['split_title'] = self.wrap_text(data['title'], width=n_cols-2)
data['split_text'] = self.wrap_text(data['text'], width=n_cols-2)
data['n_rows'] = len(data['split_title'] + data['split_text']) + 5
data['offset'] = 0
@@ -226,7 +262,7 @@ class SubmissionContent(BaseContent):
if data['type'] == 'Comment':
width = n_cols - data['offset']
data['split_body'] = wrap_text(data['body'], width=width)
data['split_body'] = self.wrap_text(data['body'], width=width)
data['n_rows'] = len(data['split_body']) + 1
else:
data['n_rows'] = 1
@@ -270,17 +306,19 @@ class SubmissionContent(BaseContent):
elif data['type'] == 'MoreComments':
with self._loader():
# Undefined behavior if using a nested loader here
assert self._loader.depth == 1
comments = data['object'].comments(update=True)
comments = self.flatten_comments(comments,
root_level=data['level'])
if not self._loader.exception:
comments = self.flatten_comments(comments, data['level'])
comment_data = [self.strip_praw_comment(c) for c in comments]
self._comment_data[index:index + 1] = comment_data
else:
raise ValueError('% type not recognized' % data['type'])
raise ValueError('%s type not recognized' % data['type'])
class SubredditContent(BaseContent):
class SubredditContent(Content):
"""
Grab a subreddit from PRAW and lazily stores submissions to an internal
list for repeat access.
@@ -300,11 +338,8 @@ class SubredditContent(BaseContent):
# don't have a real corresponding subreddit object.
try:
self.get(0)
except (praw.errors.APIException, requests.HTTPError,
praw.errors.RedirectException, praw.errors.Forbidden,
praw.errors.InvalidSubreddit, praw.errors.NotFound,
IndexError):
raise SubredditError('Could not reach subreddit %s' % name)
except IndexError:
raise exceptions.SubredditError('Unable to retrieve subreddit')
@classmethod
def from_name(cls, reddit, name, loader, order=None, query=None):
@@ -322,11 +357,11 @@ class SubredditContent(BaseContent):
display_name = '/r/{}'.format(name)
if order not in ['hot', 'top', 'rising', 'new', 'controversial', None]:
raise SubredditError('Unrecognized order "%s"' % order)
raise exceptions.SubredditError('Unrecognized order "%s"' % order)
if name == 'me':
if not reddit.is_oauth_session():
raise AccountError('Could not access user account')
raise exceptions.AccountError('Could not access user account')
elif order:
submissions = reddit.user.get_submitted(sort=order)
else:
@@ -375,25 +410,27 @@ class SubredditContent(BaseContent):
try:
with self._loader():
submission = next(self._submissions)
if self._loader.exception:
raise IndexError
except StopIteration:
raise IndexError
else:
data = self.strip_praw_submission(submission)
data['index'] = index
# Add the post number to the beginning of the title
data['title'] = u'{}. {}'.format(index+1, data['title'])
data['title'] = '{0}. {1}'.format(index+1, data['title'])
self._submission_data.append(data)
# Modifies the original dict, faster than copying
data = self._submission_data[index]
data['split_title'] = wrap_text(data['title'], width=n_cols)
data['split_title'] = self.wrap_text(data['title'], width=n_cols)
data['n_rows'] = len(data['split_title']) + 3
data['offset'] = 0
return data
class SubscriptionContent(BaseContent):
class SubscriptionContent(Content):
def __init__(self, subscriptions, loader):
@@ -403,14 +440,14 @@ class SubscriptionContent(BaseContent):
self._subscriptions = subscriptions
self._subscription_data = []
try:
self.get(0)
except IndexError:
raise exceptions.SubscriptionError('Unable to load subscriptions')
@classmethod
def from_user(cls, reddit, loader):
try:
with loader():
subscriptions = reddit.get_my_subreddits(limit=None)
except praw.errors.APIException:
raise SubscriptionError('Unable to load subscriptions')
return cls(subscriptions, loader)
def get(self, index, n_cols=70):
@@ -426,6 +463,8 @@ class SubscriptionContent(BaseContent):
try:
with self._loader():
subscription = next(self._subscriptions)
if self._loader.exception:
raise IndexError
except StopIteration:
raise IndexError
else:
@@ -433,7 +472,7 @@ class SubscriptionContent(BaseContent):
self._subscription_data.append(data)
data = self._subscription_data[index]
data['split_title'] = wrap_text(data['title'], width=n_cols)
data['split_title'] = self.wrap_text(data['title'], width=n_cols)
data['n_rows'] = len(data['split_title']) + 1
data['offset'] = 0

View File

@@ -1,368 +0,0 @@
import os
import time
import threading
import curses
from curses import textpad, ascii
from contextlib import contextmanager
from . import config
from .docs import HELP
from .helpers import strip_textpad, clean
from .exceptions import EscapeInterrupt
__all__ = ['ESCAPE', 'get_gold', 'show_notification', 'show_help',
'LoadScreen', 'Color', 'text_input', 'curses_session',
'prompt_input', 'add_line', 'get_arrow']
# Curses does define constants for symbols (e.g. curses.ACS_BULLET)
# However, they rely on using the curses.addch() function, which has been
# found to be buggy and a PITA to work with. By defining them as unicode
# points they can be added via the more reliable curses.addstr().
# http://bugs.python.org/issue21088
ESCAPE = 27
def get_gold():
"""
Return the gilded symbol.
"""
symbol = u'\u272A' if config.unicode else '*'
attr = curses.A_BOLD | Color.YELLOW
return symbol, attr
def get_arrow(likes):
"""
Return the vote symbol to display, based on the `likes` paramater.
"""
if likes is None:
symbol = u'\u2022' if config.unicode else 'o'
attr = curses.A_BOLD
elif likes:
symbol = u'\u25b2' if config.unicode else '^'
attr = curses.A_BOLD | Color.GREEN
else:
symbol = u'\u25bc' if config.unicode else 'v'
attr = curses.A_BOLD | Color.RED
return symbol, attr
def add_line(window, text, row=None, col=None, attr=None):
"""
Unicode aware version of curses's built-in addnstr method.
Safely draws a line of text on the window starting at position (row, col).
Checks the boundaries of the window and cuts off the text if it exceeds
the length of the window.
"""
# The following arg combinations must be supported to conform with addnstr
# (window, text)
# (window, text, attr)
# (window, text, row, col)
# (window, text, row, col, attr)
cursor_row, cursor_col = window.getyx()
row = row if row is not None else cursor_row
col = col if col is not None else cursor_col
max_rows, max_cols = window.getmaxyx()
n_cols = max_cols - col - 1
if n_cols <= 0:
# Trying to draw outside of the screen bounds
return
text = clean(text, n_cols)
params = [] if attr is None else [attr]
window.addstr(row, col, text, *params)
def show_notification(stdscr, message):
"""
Overlay a message box on the center of the screen and wait for user input.
Params:
message (list): List of strings, one per line.
"""
n_rows, n_cols = stdscr.getmaxyx()
box_width = max(map(len, message)) + 2
box_height = len(message) + 2
# Cut off the lines of the message that don't fit on the screen
box_width = min(box_width, n_cols)
box_height = min(box_height, n_rows)
message = message[:box_height-2]
s_row = (n_rows - box_height) // 2
s_col = (n_cols - box_width) // 2
window = stdscr.derwin(box_height, box_width, s_row, s_col)
window.erase()
window.border()
for index, line in enumerate(message, start=1):
add_line(window, line, index, 1)
window.refresh()
ch = stdscr.getch()
window.clear()
window = None
stdscr.refresh()
return ch
def show_help(stdscr):
"""
Overlay a message box with the help screen.
"""
show_notification(stdscr, HELP.splitlines())
class LoadScreen(object):
"""
Display a loading dialog while waiting for a blocking action to complete.
This class spins off a separate thread to animate the loading screen in the
background.
Usage:
#>>> loader = LoadScreen(stdscr)
#>>> with loader(...):
#>>> blocking_request(...)
"""
def __init__(self, stdscr):
self._stdscr = stdscr
self._args = None
self._animator = None
self._is_running = None
def __call__(
self,
delay=0.5,
interval=0.4,
message='Downloading',
trail='...'):
"""
Params:
delay (float): Length of time that the loader will wait before
printing on the screen. Used to prevent flicker on pages that
load very fast.
interval (float): Length of time between each animation frame.
message (str): Message to display
trail (str): Trail of characters that will be animated by the
loading screen.
"""
self._args = (delay, interval, message, trail)
return self
def __enter__(self):
self._animator = threading.Thread(target=self.animate, args=self._args)
self._animator.daemon = True
self._is_running = True
self._animator.start()
def __exit__(self, exc_type, exc_val, exc_tb):
self._is_running = False
self._animator.join()
def animate(self, delay, interval, message, trail):
start = time.time()
while (time.time() - start) < delay:
if not self._is_running:
return
message_len = len(message) + len(trail)
n_rows, n_cols = self._stdscr.getmaxyx()
s_row = (n_rows - 3) // 2
s_col = (n_cols - message_len - 1) // 2
window = self._stdscr.derwin(3, message_len + 2, s_row, s_col)
while True:
for i in range(len(trail) + 1):
if not self._is_running:
window.clear()
window = None
self._stdscr.refresh()
return
window.erase()
window.border()
window.addstr(1, 1, message + trail[:i])
window.refresh()
time.sleep(interval)
class Color(object):
"""
Color attributes for curses.
"""
_colors = {
'RED': (curses.COLOR_RED, -1),
'GREEN': (curses.COLOR_GREEN, -1),
'YELLOW': (curses.COLOR_YELLOW, -1),
'BLUE': (curses.COLOR_BLUE, -1),
'MAGENTA': (curses.COLOR_MAGENTA, -1),
'CYAN': (curses.COLOR_CYAN, -1),
'WHITE': (curses.COLOR_WHITE, -1),
}
@classmethod
def init(cls):
"""
Initialize color pairs inside of curses using the default background.
This should be called once during the curses initial setup. Afterwards,
curses color pairs can be accessed directly through class attributes.
"""
# Assign the terminal's default (background) color to code -1
curses.use_default_colors()
for index, (attr, code) in enumerate(cls._colors.items(), start=1):
curses.init_pair(index, code[0], code[1])
setattr(cls, attr, curses.color_pair(index))
@classmethod
def get_level(cls, level):
levels = [cls.MAGENTA, cls.CYAN, cls.GREEN, cls.YELLOW]
return levels[level % len(levels)]
def text_input(window, allow_resize=True):
"""
Transform a window into a text box that will accept user input and loop
until an escape sequence is entered.
If enter is pressed, return the input text as a string.
If escape is pressed, return None.
"""
window.clear()
# Set cursor mode to 1 because 2 doesn't display on some terminals
curses.curs_set(1)
# Turn insert_mode off to avoid the recursion error described here
# http://bugs.python.org/issue13051
textbox = textpad.Textbox(window, insert_mode=False)
textbox.stripspaces = 0
def validate(ch):
"Filters characters for special key sequences"
if ch == ESCAPE:
raise EscapeInterrupt
if (not allow_resize) and (ch == curses.KEY_RESIZE):
raise EscapeInterrupt
# Fix backspace for iterm
if ch == ascii.DEL:
ch = curses.KEY_BACKSPACE
return ch
# Wrapping in an exception block so that we can distinguish when the user
# hits the return character from when the user tries to back out of the
# input.
try:
out = textbox.edit(validate=validate)
except EscapeInterrupt:
out = None
curses.curs_set(0)
return strip_textpad(out)
def prompt_input(window, prompt, hide=False):
"""
Display a prompt where the user can enter text at the bottom of the screen
Set hide to True to make the input text invisible.
"""
attr = curses.A_BOLD | Color.CYAN
n_rows, n_cols = window.getmaxyx()
if hide:
prompt += ' ' * (n_cols - len(prompt) - 1)
window.addstr(n_rows-1, 0, prompt, attr)
out = window.getstr(n_rows-1, 1)
else:
window.addstr(n_rows - 1, 0, prompt, attr)
window.refresh()
subwin = window.derwin(1, n_cols - len(prompt),
n_rows - 1, len(prompt))
subwin.attrset(attr)
out = text_input(subwin)
return out
@contextmanager
def curses_session():
"""
Setup terminal and initialize curses.
"""
try:
# Curses must wait for some time after the Escape key is pressed to
# check if it is the beginning of an escape sequence indicating a
# special key. The default wait time is 1 second, which means that
# getch() will not return the escape key (27) until a full second
# after it has been pressed.
# Turn this down to 25 ms, which is close to what VIM uses.
# http://stackoverflow.com/questions/27372068
os.environ['ESCDELAY'] = '25'
# Initialize curses
stdscr = curses.initscr()
# Turn off echoing of keys, and enter cbreak mode,
# where no buffering is performed on keyboard input
curses.noecho()
curses.cbreak()
# In keypad mode, escape sequences for special keys
# (like the cursor keys) will be interpreted and
# a special value like curses.KEY_LEFT will be returned
stdscr.keypad(1)
# Start color, too. Harmless if the terminal doesn't have
# color; user can test with has_color() later on. The try/catch
# works around a minor bit of over-conscientiousness in the curses
# module -- the error return from C start_color() is ignorable.
try:
curses.start_color()
except:
pass
Color.init()
# Hide blinking cursor
curses.curs_set(0)
yield stdscr
finally:
if stdscr is not None:
stdscr.keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()

View File

@@ -1,5 +1,5 @@
__all__ = ['AGENT', 'SUMMARY', 'CONTROLS', 'HELP', 'COMMENT_FILE',
'SUBMISSION_FILE', 'COMMENT_EDIT_FILE']
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
AGENT = """\
desktop:https://github.com/michael-lazar/rtv:{version}\
@@ -49,7 +49,7 @@ Submission Mode
`SPACE` : Fold the selected comment, or load additional comments
"""
COMMENT_FILE = u"""
COMMENT_FILE = """
# Please enter a comment. Lines starting with '#' will be ignored,
# and an empty message aborts the comment.
#
@@ -57,19 +57,26 @@ COMMENT_FILE = u"""
{content}
"""
COMMENT_EDIT_FILE = u"""{content}
COMMENT_EDIT_FILE = """{content}
# Please enter a comment. Lines starting with '#' will be ignored,
# and an empty message aborts the comment.
#
# Editing your comment
"""
SUBMISSION_FILE = u"""{content}
SUBMISSION_FILE = """
# Please enter your submission. Lines starting with '#' will be ignored,
# and an empty field aborts the submission.
# and an empty message aborts the submission.
#
# The first line will be interpreted as the title
# The following lines will be interpreted as the content
#
# Posting to {name}
"""
SUBMISSION_EDIT_FILE = """{content}
# Please enter your submission. Lines starting with '#' will be ignored,
# and an empty message aborts the submission.
#
# Editing {name}
"""

View File

@@ -1,3 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class EscapeInterrupt(Exception):
"Signal that the ESC key has been pressed"

View File

@@ -1,224 +0,0 @@
import sys
import os
import curses
import webbrowser
import subprocess
from datetime import datetime
from tempfile import NamedTemporaryFile
# kitchen solves deficiencies in textwrap's handling of unicode characters
from kitchen.text.display import wrap, textual_width_chop
import six
from . import config
from .exceptions import ProgramError
__all__ = ['open_browser', 'clean', 'wrap_text', 'strip_textpad',
'strip_subreddit_url', 'humanize_timestamp', 'open_editor',
'check_browser_display']
def clean(string, n_cols=None):
"""
Required reading!
http://nedbatchelder.com/text/unipain.html
Python 2 input string will be a unicode type (unicode code points). Curses
will accept unicode if all of the points are in the ascii range. However,
if any of the code points are not valid ascii curses will throw a
UnicodeEncodeError: 'ascii' codec can't encode character, ordinal not in
range(128). If we encode the unicode to a utf-8 byte string and pass that
to curses, it will render correctly.
Python 3 input string will be a string type (unicode code points). Curses
will accept that in all cases. However, the n character count in addnstr
will not be correct. If code points are passed to addnstr, curses will
treat each code point as one character and will not account for wide
characters. If utf-8 is passed in, addnstr will treat each 'byte' as a
single character.
"""
if n_cols is not None and n_cols <= 0:
return ''
if not config.unicode:
if six.PY3 or isinstance(string, unicode):
string = string.encode('ascii', 'replace')
return string[:n_cols] if n_cols else string
else:
if n_cols:
string = textual_width_chop(string, n_cols)
if six.PY3 or isinstance(string, unicode):
string = string.encode('utf-8')
return string
def open_editor(data=''):
"""
Open a temporary file using the system's default editor.
The data string will be written to the file before opening. This function
will block until the editor has closed. At that point the file will be
read and and lines starting with '#' will be stripped.
"""
with NamedTemporaryFile(prefix='rtv-', suffix='.txt', mode='wb') as fp:
fp.write(clean(data))
fp.flush()
editor = os.getenv('RTV_EDITOR') or os.getenv('EDITOR') or 'nano'
curses.endwin()
try:
subprocess.Popen([editor, fp.name]).wait()
except OSError:
raise ProgramError('Could not open file with %s' % editor)
curses.doupdate()
# Open a second file object to read. This appears to be necessary in
# order to read the changes made by some editors (gedit). w+ mode does
# not work!
with open(fp.name) as fp2:
text = ''.join(line for line in fp2 if not line.startswith('#'))
text = text.rstrip()
return text
def open_browser(url):
"""
Open the given url using the default webbrowser. The preferred browser can
specified with the $BROWSER environment variable. If not specified, python
webbrowser will try to determine the default to use based on your system.
For browsers requiring an X display, we call webbrowser.open_new_tab(url)
and redirect stdout/stderr to devnull. This is a workaround to stop firefox
from spewing warning messages to the console. See
http://bugs.python.org/issue22277 for a better description of the problem.
For console browsers (e.g. w3m), RTV will suspend and display the browser
window within the same terminal. This mode is triggered either when
1. $BROWSER is set to a known console browser, or
2. $DISPLAY is undefined, indicating that the terminal is running headless
There may be other cases where console browsers are opened (xdg-open?) but
are not detected here.
"""
if check_browser_display():
command = "import webbrowser; webbrowser.open_new_tab('%s')" % url
args = [sys.executable, '-c', command]
with open(os.devnull, 'ab+', 0) as null:
subprocess.check_call(args, stdout=null, stderr=null)
else:
curses.endwin()
webbrowser.open_new_tab(url)
curses.doupdate()
def check_browser_display():
"""
Use a number of methods to guess if the default webbrowser will open in
the background as opposed to opening directly in the terminal.
"""
display = bool(os.environ.get("DISPLAY"))
# Use the convention defined here to parse $BROWSER
# https://docs.python.org/2/library/webbrowser.html
console_browsers = ['www-browser', 'links', 'links2', 'elinks', 'lynx',
'w3m']
if "BROWSER" in os.environ:
user_browser = os.environ["BROWSER"].split(os.pathsep)[0]
if user_browser in console_browsers:
display = False
if webbrowser._tryorder and webbrowser._tryorder[0] in console_browsers:
display = False
return display
def wrap_text(text, width):
"""
Wrap text paragraphs to the given character width while preserving
newlines.
"""
out = []
for paragraph in text.splitlines():
# Wrap returns an empty list when paragraph is a newline. In order to
# preserve newlines we substitute a list containing an empty string.
lines = wrap(paragraph, width=width) or ['']
out.extend(lines)
return out
def strip_textpad(text):
"""
Attempt to intelligently strip excess whitespace from the output of a
curses textpad.
"""
if text is None:
return text
# Trivial case where the textbox is only one line long.
if '\n' not in text:
return text.rstrip()
# Allow one space at the end of the line. If there is more than one space,
# assume that a newline operation was intended by the user
stack, current_line = [], ''
for line in text.split('\n'):
if line.endswith(' '):
stack.append(current_line + line.rstrip())
current_line = ''
else:
current_line += line
stack.append(current_line)
# Prune empty lines at the bottom of the textbox.
for item in stack[::-1]:
if len(item) == 0:
stack.pop()
else:
break
out = '\n'.join(stack)
return out
def strip_subreddit_url(permalink):
"""
Strip a subreddit name from the subreddit's permalink.
This is used to avoid submission.subreddit.url making a separate API call.
"""
subreddit = permalink.split('/')[4]
return '/r/{}'.format(subreddit)
def humanize_timestamp(utc_timestamp, verbose=False):
"""
Convert a utc timestamp into a human readable relative-time.
"""
timedelta = datetime.utcnow() - datetime.utcfromtimestamp(utc_timestamp)
seconds = int(timedelta.total_seconds())
if seconds < 60:
return 'moments ago' if verbose else '0min'
minutes = seconds // 60
if minutes < 60:
return ('%d minutes ago' % minutes) if verbose else ('%dmin' % minutes)
hours = minutes // 60
if hours < 24:
return ('%d hours ago' % hours) if verbose else ('%dhr' % hours)
days = hours // 24
if days < 30:
return ('%d days ago' % days) if verbose else ('%dday' % days)
months = days // 30.4
if months < 12:
return ('%d months ago' % months) if verbose else ('%dmonth' % months)
years = months // 12
return ('%d years ago' % years) if verbose else ('%dyr' % years)

View File

@@ -1,70 +0,0 @@
import os
__all__ = ['load_history', 'save_history']
def history_path():
"""
Create the path to the history log
"""
HOME = os.path.expanduser('~')
XDG_CONFIG_HOME = os.getenv('XDG_CACHE_HOME',
os.path.join(HOME, '.config'))
path = os.path.join(XDG_CONFIG_HOME, 'rtv')
if not os.path.exists(path):
os.makedirs(path)
return os.path.join(path, 'history.log')
def load_history():
"""
Load the history file into memory if it exists
"""
path = history_path()
if os.path.exists(path):
with open(path) as history_file:
# reverse the list so the newest ones are first
history = [line.strip() for line in history_file][::-1]
return OrderedSet(history)
return OrderedSet()
def save_history(history):
"""
Save the visited links to the history log
"""
path = history_path()
with open(path, 'w+') as history_file:
for i in range(200):
if not history:
break
try:
history_file.write(history.pop() + '\n')
except UnicodeEncodeError:
# Ignore unicode URLS, may want to handle this at some point
continue
class OrderedSet(object):
"""
A simple implementation of an ordered set. A set is used to check
for membership, and a list is used to maintain ordering.
"""
def __init__(self, elements=[]):
self._set = set(elements)
self._list = elements
def __contains__(self, item):
return item in self._set
def __len__(self):
return len(self._list)
def add(self, item):
self._set.add(item)
self._list.append(item)
def pop(self):
return self._list.pop()

View File

@@ -1,127 +1,143 @@
import os
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import uuid
import praw
from tornado import gen, ioloop, web, httpserver
from concurrent.futures import ThreadPoolExecutor
from . import config
from .curses_helpers import show_notification
from .helpers import check_browser_display, open_browser
__all__ = ['OAuthTool']
class OAuthHandler(web.RequestHandler):
"""
Intercepts the redirect that Reddit sends the user to after they verify or
deny the application access.
oauth_state = None
oauth_code = None
oauth_error = None
The GET should supply 3 request params:
state: Unique id that was supplied by us at the beginning of the
process to verify that the session matches.
code: Code that we can use to generate the refresh token.
error: If an error occurred, it will be placed here.
"""
template_path = os.path.join(os.path.dirname(__file__), 'templates')
class AuthHandler(web.RequestHandler):
def initialize(self, display=None, params=None):
self.display = display
self.params = params
def get(self):
global oauth_state, oauth_code, oauth_error
self.params['state'] = self.get_argument('state', default=None)
self.params['code'] = self.get_argument('code', default=None)
self.params['error'] = self.get_argument('error', default=None)
oauth_state = self.get_argument('state', default='placeholder')
oauth_code = self.get_argument('code', default='placeholder')
oauth_error = self.get_argument('error', default='placeholder')
self.render('index.html', state=oauth_state, code=oauth_code,
error=oauth_error)
self.render('index.html', **self.params)
complete = self.params['state'] and self.params['code']
if complete or self.params['error']:
# Stop IOLoop if using a background browser such as firefox
if check_browser_display():
if self.display:
ioloop.IOLoop.current().stop()
class OAuthTool(object):
class OAuthHelper(object):
def __init__(self, reddit, stdscr=None, loader=None):
def __init__(self, reddit, term, config):
self.term = term
self.reddit = reddit
self.stdscr = stdscr
self.loader = loader
self.http_server = None
self.config = config
self.refresh_token = config.load_refresh_token()
self.http_server = None
self.params = {'state': None, 'code': None, 'error': None}
# Initialize Tornado webapp
routes = [('/', AuthHandler)]
self.callback_app = web.Application(routes,
template_path=template_path)
# Pass a mutable params object so the request handler can modify it
kwargs = {'display': self.term.display, 'params': self.params}
routes = [('/', OAuthHandler, kwargs)]
self.callback_app = web.Application(
routes, template_path=self.config['template_path'])
self.reddit.set_oauth_app_info(config.oauth_client_id,
config.oauth_client_secret,
config.oauth_redirect_uri)
self.reddit.set_oauth_app_info(
self.config['oauth_client_id'],
self.config['oauth_client_secret'],
self.config['oauth_redirect_uri'])
# Reddit's mobile website works better on terminal browsers
if not check_browser_display():
if not self.term.display:
if '.compact' not in self.reddit.config.API_PATHS['authorize']:
self.reddit.config.API_PATHS['authorize'] += '.compact'
def authorize(self):
self.params.update(state=None, code=None, error=None)
# If we already have a token, request new access credentials
if self.refresh_token:
with self.loader(message='Logging in'):
self.reddit.refresh_access_information(self.refresh_token)
if self.config.refresh_token:
with self.term.loader(message='Logging in'):
self.reddit.refresh_access_information(
self.config.refresh_token)
return
# https://github.com/tornadoweb/tornado/issues/1420
io = ioloop.IOLoop.current()
# Start the authorization callback server
if self.http_server is None:
self.http_server = httpserver.HTTPServer(self.callback_app)
self.http_server.listen(config.oauth_redirect_port)
self.http_server.listen(self.config['oauth_redirect_port'])
hex_uuid = uuid.uuid4().hex
state = uuid.uuid4().hex
authorize_url = self.reddit.get_authorize_url(
hex_uuid, scope=config.oauth_scope, refreshable=True)
state, scope=self.config['oauth_scope'], refreshable=True)
# Open the browser and wait for the user to authorize the app
if check_browser_display():
with self.loader(message='Waiting for authorization'):
open_browser(authorize_url)
ioloop.IOLoop.current().start()
if self.term.display:
# Open a background browser (e.g. firefox) which is non-blocking.
# Stop the iloop when the user hits the auth callback, at which
# point we continue and check the callback params.
with self.term.loader(message='Opening browser for authorization'):
self.term.open_browser(authorize_url)
io.start()
if self.term.loader.exception:
io.clear_instance()
return
else:
with self.loader(delay=0, message='Redirecting to reddit'):
# Provide user feedback
# Open the terminal webbrowser in a background thread and wait
# while for the user to close the process. Once the process is
# closed, the iloop is stopped and we can check if the user has
# hit the callback URL.
with self.term.loader(delay=0, message='Redirecting to reddit'):
# This load message exists to provide user feedback
time.sleep(1)
ioloop.IOLoop.current().add_callback(self._open_authorize_url,
authorize_url)
ioloop.IOLoop.current().start()
io.add_callback(self._async_open_browser, authorize_url)
io.start()
if oauth_error == 'access_denied':
show_notification(self.stdscr, ['Declined access'])
if self.params['error'] == 'access_denied':
self.term.show_notification('Declined access')
return
elif oauth_error != 'placeholder':
show_notification(self.stdscr, ['Authentication error'])
elif self.params['error']:
self.term.show_notification('Authentication error')
return
elif hex_uuid != oauth_state:
# Check if UUID matches obtained state.
# If not, authorization process is compromised.
show_notification(self.stdscr, ['UUID mismatch'])
elif self.params['state'] != state:
self.term.show_notification('UUID mismatch')
return
try:
with self.loader(message='Logging in'):
access_info = self.reddit.get_access_information(oauth_code)
self.refresh_token = access_info['refresh_token']
if config.persistent:
config.save_refresh_token(access_info['refresh_token'])
except (praw.errors.OAuthAppRequired, praw.errors.OAuthInvalidToken):
show_notification(self.stdscr, ['Invalid OAuth data'])
else:
message = ['Welcome {}!'.format(self.reddit.user.name)]
show_notification(self.stdscr, message)
with self.term.loader(message='Logging in'):
info = self.reddit.get_access_information(self.params['code'])
if self.term.loader.exception:
return
message = 'Welcome {}!'.format(self.reddit.user.name)
self.term.show_notification(message)
self.config.refresh_token = info['refresh_token']
if self.config['persistent']:
self.config.save_refresh_token()
def clear_oauth_data(self):
self.reddit.clear_authentication()
config.clear_refresh_token()
self.refresh_token = None
self.config.delete_refresh_token()
@gen.coroutine
def _open_authorize_url(self, url):
def _async_open_browser(self, url):
with ThreadPoolExecutor(max_workers=1) as executor:
yield executor.submit(open_browser, url)
yield executor.submit(self.term.open_browser, url)
ioloop.IOLoop.current().stop()

554
rtv/objects.py Normal file
View File

@@ -0,0 +1,554 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import time
import curses
import signal
import inspect
import weakref
import logging
import threading
from contextlib import contextmanager
import six
import praw
import requests
from . import exceptions
_logger = logging.getLogger(__name__)
@contextmanager
def curses_session():
"""
Setup terminal and initialize curses. Most of this copied from
curses.wrapper in order to convert the wrapper into a context manager.
"""
try:
# Curses must wait for some time after the Escape key is pressed to
# check if it is the beginning of an escape sequence indicating a
# special key. The default wait time is 1 second, which means that
# http://stackoverflow.com/questions/27372068
os.environ['ESCDELAY'] = '25'
# Initialize curses
stdscr = curses.initscr()
# Turn off echoing of keys, and enter cbreak mode, where no buffering
# is performed on keyboard input
curses.noecho()
curses.cbreak()
# In keypad mode, escape sequences for special keys (like the cursor
# keys) will be interpreted and a special value like curses.KEY_LEFT
# will be returned
stdscr.keypad(1)
# Start color, too. Harmless if the terminal doesn't have color; user
# can test with has_color() later on. The try/catch works around a
# minor bit of over-conscientiousness in the curses module -- the error
# return from C start_color() is ignorable.
try:
curses.start_color()
except:
pass
# Hide the blinking cursor
curses.curs_set(0)
Color.init()
yield stdscr
finally:
if 'stdscr' in locals():
stdscr.keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()
class LoadScreen(object):
"""
Display a loading dialog while waiting for a blocking action to complete.
This class spins off a separate thread to animate the loading screen in the
background. The loading thread also takes control of stdscr.getch(). If
an exception occurs in the main thread while the loader is active, the
exception will be caught, attached to the loader object, and displayed as
a notification. The attached exception can be used to trigger context
sensitive actions. For example, if the connection hangs while opening a
submission, the user may press ctrl-c to raise a KeyboardInterrupt. In this
case we would *not* want to refresh the current page.
>>> with self.terminal.loader(...) as loader:
>>> # Perform a blocking request to load content
>>> blocking_request(...)
>>>
>>> if loader.exception is None:
>>> # Only run this if the load was successful
>>> self.refresh_content()
When a loader is nested inside of itself, the outermost loader takes
priority and all of the nested loaders become no-ops. Call arguments given
to nested loaders will be ignored, and errors will propagate to the parent.
>>> with self.terminal.loader(...) as loader:
>>>
>>> # Additional loaders will be ignored
>>> with self.terminal.loader(...):
>>> raise KeyboardInterrupt()
>>>
>>> # This code will not be executed because the inner loader doesn't
>>> # catch the exception
>>> assert False
>>>
>>> # The exception is finally caught by the outer loader
>>> assert isinstance(terminal.loader.exception, KeyboardInterrupt)
"""
HANDLED_EXCEPTIONS = [
(exceptions.SubscriptionError, 'No Subscriptions'),
(exceptions.AccountError, 'Unable to Access Account'),
(exceptions.SubredditError, 'Invalid Subreddit'),
(praw.errors.InvalidSubreddit, 'Invalid Subreddit'),
(praw.errors.InvalidComment, 'Invalid Comment'),
(praw.errors.InvalidSubmission, 'Invalid Submission'),
(praw.errors.OAuthAppRequired, 'Invalid OAuth data'),
(praw.errors.OAuthException, 'Invalid OAuth data'),
(praw.errors.LoginOrScopeRequired, 'Not Logged In'),
(praw.errors.ClientException, 'Reddit Client Error'),
(praw.errors.NotFound, 'Not Found'),
(praw.errors.APIException, 'Reddit API Error'),
(praw.errors.HTTPException, 'Reddit HTTP Error'),
(requests.HTTPError, 'Unexpected HTTP Error'),
(requests.ConnectionError, 'Connection Error'),
(KeyboardInterrupt, None),
]
def __init__(self, terminal):
self.exception = None
self.catch_exception = None
self.depth = 0
self._terminal = weakref.proxy(terminal)
self._args = None
self._animator = None
self._is_running = None
def __call__(self, delay=0.5, interval=0.4, message='Downloading',
trail='...', catch_exception=True):
"""
Params:
delay (float): Length of time that the loader will wait before
printing on the screen. Used to prevent flicker on pages that
load very fast.
interval (float): Length of time between each animation frame.
message (str): Message to display
trail (str): Trail of characters that will be animated by the
loading screen.
catch_exception (bool): If an exception occurs while the loader is
active, this flag determines whether it is caught or allowed to
bubble up.
"""
if self.depth > 0:
return self
self.exception = None
self.catch_exception = catch_exception
self._args = (delay, interval, message, trail)
return self
def __enter__(self):
self.depth += 1
if self.depth > 1:
return self
self._animator = threading.Thread(target=self.animate, args=self._args)
self._animator.daemon = True
self._is_running = True
self._animator.start()
return self
def __exit__(self, exc_type, e, exc_tb):
self.depth -= 1
if self.depth > 0:
return
self._is_running = False
self._animator.join()
self._terminal.stdscr.refresh()
if self.catch_exception and e is not None:
# Log the exception and attach it so the caller can inspect it
self.exception = e
_logger.info('Loader caught: {0} - {1}'.format(type(e).__name__, e))
# If an error occurred, display a notification on the screen
for base, message in self.HANDLED_EXCEPTIONS:
if isinstance(e, base):
if message:
self._terminal.show_notification(message)
break
else:
return # Re-raise unhandled exceptions
return True # Otherwise swallow the exception and continue
def animate(self, delay, interval, message, trail):
# The animation starts with a configurable delay before drawing on the
# screen. This is to prevent very short loading sections from
# flickering on the screen before immediately disappearing.
with self._terminal.no_delay():
start = time.time()
while (time.time() - start) < delay:
# Pressing escape triggers a keyboard interrupt
if self._terminal.getch() == self._terminal.ESCAPE:
os.kill(os.getpid(), signal.SIGINT)
self._is_running = False
if not self._is_running:
return
time.sleep(0.01)
# Build the notification window
message_len = len(message) + len(trail)
n_rows, n_cols = self._terminal.stdscr.getmaxyx()
s_row = (n_rows - 3) // 2
s_col = (n_cols - message_len - 1) // 2
window = curses.newwin(3, message_len + 2, s_row, s_col)
# Animate the loading prompt until the stopping condition is triggered
# when the context manager exits.
with self._terminal.no_delay():
while True:
for i in range(len(trail) + 1):
if not self._is_running:
window.erase()
del window
self._terminal.stdscr.touchwin()
self._terminal.stdscr.refresh()
return
window.erase()
window.border()
self._terminal.add_line(window, message + trail[:i], 1, 1)
window.refresh()
# Break up the designated sleep interval into smaller
# chunks so we can more responsively check for interrupts.
for _ in range(int(interval/0.01)):
# Pressing escape triggers a keyboard interrupt
if self._terminal.getch() == self._terminal.ESCAPE:
os.kill(os.getpid(), signal.SIGINT)
self._is_running = False
break
time.sleep(0.01)
class Color(object):
"""
Color attributes for curses.
"""
RED = curses.A_NORMAL
GREEN = curses.A_NORMAL
YELLOW = curses.A_NORMAL
BLUE = curses.A_NORMAL
MAGENTA = curses.A_NORMAL
CYAN = curses.A_NORMAL
WHITE = curses.A_NORMAL
_colors = {
'RED': (curses.COLOR_RED, -1),
'GREEN': (curses.COLOR_GREEN, -1),
'YELLOW': (curses.COLOR_YELLOW, -1),
'BLUE': (curses.COLOR_BLUE, -1),
'MAGENTA': (curses.COLOR_MAGENTA, -1),
'CYAN': (curses.COLOR_CYAN, -1),
'WHITE': (curses.COLOR_WHITE, -1),
}
@classmethod
def init(cls):
"""
Initialize color pairs inside of curses using the default background.
This should be called once during the curses initial setup. Afterwards,
curses color pairs can be accessed directly through class attributes.
"""
# Assign the terminal's default (background) color to code -1
curses.use_default_colors()
for index, (attr, code) in enumerate(cls._colors.items(), start=1):
curses.init_pair(index, code[0], code[1])
setattr(cls, attr, curses.color_pair(index))
@classmethod
def get_level(cls, level):
levels = [cls.MAGENTA, cls.CYAN, cls.GREEN, cls.YELLOW]
return levels[level % len(levels)]
class Navigator(object):
"""
Handles the math behind cursor movement and screen paging.
This class determines how cursor movements effect the currently displayed
page. For example, if scrolling down the page, items are drawn from the
bottom up. This ensures that the item at the very bottom of the screen
(the one selected by cursor) will be fully drawn and not cut off. Likewise,
when scrolling up the page, items are drawn from the top down. If the
cursor is moved around without hitting the top or bottom of the screen, the
current mode is preserved.
"""
def __init__(
self,
valid_page_cb,
page_index=0,
cursor_index=0,
inverted=False):
"""
Params:
valid_page_callback (func): This function, usually `Content.get`,
takes a page index and raises an IndexError if that index falls
out of bounds. This is used to determine the upper and lower
bounds of the page, i.e. when to stop scrolling.
page_index (int): Initial page index.
cursor_index (int): Initial cursor index, relative to the page.
inverted (bool): Whether the page scrolling is reversed of not.
normal - The page is drawn from the top of the screen,
starting with the page index, down to the bottom of
the screen.
inverted - The page is drawn from the bottom of the screen,
starting with the page index, up to the top of the
screen.
"""
self.page_index = page_index
self.cursor_index = cursor_index
self.inverted = inverted
self._page_cb = valid_page_cb
@property
def step(self):
return 1 if not self.inverted else -1
@property
def position(self):
return self.page_index, self.cursor_index, self.inverted
@property
def absolute_index(self):
"""
Return the index of the currently selected item.
"""
return self.page_index + (self.step * self.cursor_index)
def move(self, direction, n_windows):
"""
Move the cursor up or down by the given increment.
Params:
direction (int): `1` will move the cursor down one item and `-1`
will move the cursor up one item.
n_windows (int): The number of items that are currently being drawn
on the screen.
Returns:
valid (bool): Indicates whether or not the attempted cursor move is
allowed. E.g. When the cursor is on the last comment,
attempting to scroll down any further would not be valid.
redraw (bool): Indicates whether or not the screen needs to be
redrawn.
"""
assert direction in (-1, 1)
valid, redraw = True, False
forward = ((direction * self.step) > 0)
if forward:
if self.page_index < 0:
if self._is_valid(0):
# Special case - advance the page index if less than zero
self.page_index = 0
self.cursor_index = 0
redraw = True
else:
valid = False
else:
self.cursor_index += 1
if not self._is_valid(self.absolute_index):
# Move would take us out of bounds
self.cursor_index -= 1
valid = False
elif self.cursor_index >= (n_windows - 1):
# Flip the orientation and reset the cursor
self.flip(self.cursor_index)
self.cursor_index = 0
redraw = True
else:
if self.cursor_index > 0:
self.cursor_index -= 1
else:
self.page_index -= self.step
if self._is_valid(self.absolute_index):
# We have reached the beginning of the page - move the
# index
redraw = True
else:
self.page_index += self.step
valid = False # Revert
return valid, redraw
def move_page(self, direction, n_windows):
"""
Move the page down (positive direction) or up (negative direction).
Paging down:
The post on the bottom of the page becomes the post at the top of
the page and the cursor is moved to the top.
Paging up:
The post at the top of the page becomes the post at the bottom of
the page and the cursor is moved to the bottom.
"""
assert direction in (-1, 1)
assert n_windows >= 0
# top of subreddit/submission page or only one
# submission/reply on the screen: act as normal move
if (self.absolute_index < 0) | (n_windows == 0):
valid, redraw = self.move(direction, n_windows)
else:
# first page
if self.absolute_index < n_windows and direction < 0:
self.page_index = -1
self.cursor_index = 0
self.inverted = False
# not submission mode: starting index is 0
if not self._is_valid(self.absolute_index):
self.page_index = 0
valid = True
else:
# flip to the direction of movement
if ((direction > 0) & (self.inverted is True))\
| ((direction < 0) & (self.inverted is False)):
self.page_index += (self.step * (n_windows-1))
self.inverted = not self.inverted
self.cursor_index \
= (n_windows-(direction < 0)) - self.cursor_index
valid = False
adj = 0
# check if reached the bottom
while not valid:
n_move = n_windows - adj
if n_move == 0:
break
self.page_index += n_move * direction
valid = self._is_valid(self.absolute_index)
if not valid:
self.page_index -= n_move * direction
adj += 1
redraw = True
return valid, redraw
def flip(self, n_windows):
"""
Flip the orientation of the page.
"""
assert n_windows >= 0
self.page_index += (self.step * n_windows)
self.cursor_index = n_windows
self.inverted = not self.inverted
def _is_valid(self, page_index):
"""
Check if a page index will cause entries to fall outside valid range.
"""
try:
self._page_cb(page_index)
except IndexError:
return False
else:
return True
class Controller(object):
"""
Event handler for triggering functions with curses keypresses.
Register a keystroke to a class method using the @register decorator.
>>> @Controller.register('a', 'A')
>>> def func(self, *args)
>>> ...
Register a default behavior by using `None`.
>>> @Controller.register(None)
>>> def default_func(self, *args)
>>> ...
Bind the controller to a class instance and trigger a key. Additional
arguments will be passed to the function.
>>> controller = Controller(self)
>>> controller.trigger('a', *args)
"""
character_map = {}
def __init__(self, instance):
self.instance = instance
# Build a list of parent controllers that follow the object's MRO to
# check if any parent controllers have registered the keypress
self.parents = inspect.getmro(type(self))[:-1]
def trigger(self, char, *args, **kwargs):
if isinstance(char, six.string_types) and len(char) == 1:
char = ord(char)
func = None
# Check if the controller (or any of the controller's parents) have
# registered a function to the given key
for controller in self.parents:
if func:
break
func = controller.character_map.get(char)
# If the controller has not registered the key, check if there is a
# default function registered
for controller in self.parents:
if func:
break
func = controller.character_map.get(None)
return func(self.instance, *args, **kwargs) if func else None
@classmethod
def register(cls, *chars):
def inner(f):
for char in chars:
if isinstance(char, six.string_types) and len(char) == 1:
cls.character_map[ord(char)] = f
else:
cls.character_map[char] = f
return f
return inner

View File

@@ -1,257 +1,47 @@
import curses
import time
import six
import sys
import logging
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
import time
import curses
from functools import wraps
import praw.errors
import requests
from kitchen.text.display import textual_width
from .helpers import open_editor
from .curses_helpers import (Color, show_notification, show_help, prompt_input,
add_line)
from .docs import COMMENT_EDIT_FILE, SUBMISSION_FILE
__all__ = ['Navigator', 'BaseController', 'BasePage']
_logger = logging.getLogger(__name__)
from . import docs
from .objects import Controller, Color
class Navigator(object):
def logged_in(f):
"""
Handles math behind cursor movement and screen paging.
Decorator for Page methods that require the user to be authenticated.
"""
def __init__(
self,
valid_page_cb,
page_index=0,
cursor_index=0,
inverted=False):
self.page_index = page_index
self.cursor_index = cursor_index
self.inverted = inverted
self._page_cb = valid_page_cb
self._header_window = None
self._content_window = None
@property
def step(self):
return 1 if not self.inverted else -1
@property
def position(self):
return (self.page_index, self.cursor_index, self.inverted)
@property
def absolute_index(self):
return self.page_index + (self.step * self.cursor_index)
def move(self, direction, n_windows):
"Move the cursor down (positive direction) or up (negative direction)"
valid, redraw = True, False
forward = ((direction * self.step) > 0)
if forward:
if self.page_index < 0:
if self._is_valid(0):
# Special case - advance the page index if less than zero
self.page_index = 0
self.cursor_index = 0
redraw = True
else:
valid = False
else:
self.cursor_index += 1
if not self._is_valid(self.absolute_index):
# Move would take us out of bounds
self.cursor_index -= 1
valid = False
elif self.cursor_index >= (n_windows - 1):
# Flip the orientation and reset the cursor
self.flip(self.cursor_index)
self.cursor_index = 0
redraw = True
else:
if self.cursor_index > 0:
self.cursor_index -= 1
else:
self.page_index -= self.step
if self._is_valid(self.absolute_index):
# We have reached the beginning of the page - move the
# index
redraw = True
else:
self.page_index += self.step
valid = False # Revert
return valid, redraw
def move_page(self, direction, n_windows):
"""
Move page down (positive direction) or up (negative direction).
"""
# top of subreddit/submission page or only one
# submission/reply on the screen: act as normal move
if (self.absolute_index < 0) | (n_windows == 0):
valid, redraw = self.move(direction, n_windows)
else:
# first page
if self.absolute_index < n_windows and direction < 0:
self.page_index = -1
self.cursor_index = 0
self.inverted = False
# not submission mode: starting index is 0
if not self._is_valid(self.absolute_index):
self.page_index = 0
valid = True
else:
# flip to the direction of movement
if ((direction > 0) & (self.inverted is True))\
| ((direction < 0) & (self.inverted is False)):
self.page_index += (self.step * (n_windows-1))
self.inverted = not self.inverted
self.cursor_index \
= (n_windows-(direction < 0)) - self.cursor_index
valid = False
adj = 0
# check if reached the bottom
while not valid:
n_move = n_windows - adj
if n_move == 0:
break
self.page_index += n_move * direction
valid = self._is_valid(self.absolute_index)
if not valid:
self.page_index -= n_move * direction
adj += 1
redraw = True
return valid, redraw
def flip(self, n_windows):
"Flip the orientation of the page"
self.page_index += (self.step * n_windows)
self.cursor_index = n_windows
self.inverted = not self.inverted
def _is_valid(self, page_index):
"Check if a page index will cause entries to fall outside valid range"
try:
self._page_cb(page_index)
except IndexError:
return False
else:
return True
@wraps(f)
def wrapped_method(self, *args, **kwargs):
if not self.reddit.is_oauth_session():
self.term.show_notification('Not logged in')
return
return f(self, *args, **kwargs)
return wrapped_method
class SafeCaller(object):
def __init__(self, window):
self.window = window
self.catch = True
def __enter__(self):
return self
def __exit__(self, exc_type, e, exc_tb):
if self.catch:
if isinstance(e, praw.errors.APIException):
message = ['Error: {}'.format(e.error_type), e.message]
show_notification(self.window, message)
_logger.exception(e)
return True
elif isinstance(e, praw.errors.ClientException):
message = ['Error: Client Exception', e.message]
show_notification(self.window, message)
_logger.exception(e)
return True
elif isinstance(e, requests.HTTPError):
show_notification(self.window, ['Unexpected Error'])
_logger.exception(e)
return True
elif isinstance(e, requests.ConnectionError):
show_notification(self.window, ['Unexpected Error'])
_logger.exception(e)
return True
class PageController(Controller):
character_map = {}
class BaseController(object):
"""
Event handler for triggering functions with curses keypresses.
class Page(object):
Register a keystroke to a class method using the @egister decorator.
#>>> @Controller.register('a', 'A')
#>>> def func(self, *args)
def __init__(self, reddit, term, config, oauth):
Register a default behavior by using `None`.
#>>> @Controller.register(None)
#>>> def default_func(self, *args)
Bind the controller to a class instance and trigger a key. Additional
arguments will be passed to the function.
#>>> controller = Controller(self)
#>>> controller.trigger('a', *args)
"""
character_map = {None: (lambda *args, **kwargs: None)}
def __init__(self, instance):
self.instance = instance
def trigger(self, char, *args, **kwargs):
if isinstance(char, six.string_types) and len(char) == 1:
char = ord(char)
func = self.character_map.get(char)
if func is None:
func = BaseController.character_map.get(char)
if func is None:
func = self.character_map.get(None)
if func is None:
func = BaseController.character_map.get(None)
return func(self.instance, *args, **kwargs)
@classmethod
def register(cls, *chars):
def wrap(f):
for char in chars:
if isinstance(char, six.string_types) and len(char) == 1:
cls.character_map[ord(char)] = f
else:
cls.character_map[char] = f
return f
return wrap
class BasePage(object):
"""
Base terminal viewer incorporates a cursor to navigate content
"""
MIN_HEIGHT = 10
MIN_WIDTH = 20
def __init__(self, stdscr, reddit, content, oauth, **kwargs):
self.stdscr = stdscr
self.reddit = reddit
self.content = content
self.term = term
self.config = config
self.oauth = oauth
self.nav = Navigator(self.content.get, **kwargs)
self.content = None
self.nav = None
self.controller = None
self.active = True
self._header_window = None
self._content_window = None
self._subwindows = None
@@ -260,101 +50,114 @@ class BasePage(object):
raise NotImplementedError
@staticmethod
def draw_item(window, data, inverted):
def _draw_item(window, data, inverted):
raise NotImplementedError
@BaseController.register('q')
def loop(self):
"""
Main control loop runs the following steps:
1. Re-draw the screen
2. Wait for user to press a key (includes terminal resizing)
3. Trigger the method registered to the input key
The loop will run until self.active is set to False from within one of
the methods.
"""
self.active = True
while self.active:
self.draw()
ch = self.term.stdscr.getch()
self.controller.trigger(ch)
@PageController.register('q')
def exit(self):
"""
Prompt to exit the application.
"""
ch = prompt_input(self.stdscr, "Do you really want to quit? (y/n): ")
if ch == 'y':
if self.term.prompt_y_or_n('Do you really want to quit? (y/n): '):
sys.exit()
elif ch != 'n':
curses.flash()
@BaseController.register('Q')
@PageController.register('Q')
def force_exit(self):
sys.exit()
@BaseController.register('?')
def help(self):
show_help(self._content_window)
@PageController.register('?')
def show_help(self):
self.term.show_notification(docs.HELP.strip().splitlines())
@BaseController.register('1')
@PageController.register('1')
def sort_content_hot(self):
self.refresh_content(order='hot')
@BaseController.register('2')
@PageController.register('2')
def sort_content_top(self):
self.refresh_content(order='top')
@BaseController.register('3')
@PageController.register('3')
def sort_content_rising(self):
self.refresh_content(order='rising')
@BaseController.register('4')
@PageController.register('4')
def sort_content_new(self):
self.refresh_content(order='new')
@BaseController.register('5')
@PageController.register('5')
def sort_content_controversial(self):
self.refresh_content(order='controversial')
@BaseController.register(curses.KEY_UP, 'k')
@PageController.register(curses.KEY_UP, 'k')
def move_cursor_up(self):
self._move_cursor(-1)
self.clear_input_queue()
@BaseController.register(curses.KEY_DOWN, 'j')
@PageController.register(curses.KEY_DOWN, 'j')
def move_cursor_down(self):
self._move_cursor(1)
self.clear_input_queue()
@BaseController.register('n', curses.KEY_NPAGE)
def move_page_down(self):
self._move_page(1)
self.clear_input_queue()
@BaseController.register('m', curses.KEY_PPAGE)
@PageController.register('m', curses.KEY_PPAGE)
def move_page_up(self):
self._move_page(-1)
self.clear_input_queue()
@BaseController.register('a')
@PageController.register('n', curses.KEY_NPAGE)
def move_page_down(self):
self._move_page(1)
self.clear_input_queue()
@PageController.register('a')
@logged_in
def upvote(self):
data = self.content.get(self.nav.absolute_index)
try:
if 'likes' not in data:
pass
self.term.flash()
elif data['likes']:
with self.term.loader():
data['object'].clear_vote()
if not self.term.loader.exception:
data['likes'] = None
else:
with self.term.loader():
data['object'].upvote()
if not self.term.loader.exception:
data['likes'] = True
except praw.errors.LoginOrScopeRequired:
show_notification(self.stdscr, ['Not logged in'])
@BaseController.register('z')
@PageController.register('z')
@logged_in
def downvote(self):
data = self.content.get(self.nav.absolute_index)
try:
if 'likes' not in data:
pass
self.term.flash()
elif data['likes'] or data['likes'] is None:
with self.term.loader():
data['object'].downvote()
if not self.term.loader.exception:
data['likes'] = False
else:
with self.term.loader():
data['object'].clear_vote()
if not self.term.loader.exception:
data['likes'] = None
except praw.errors.LoginOrScopeRequired:
show_notification(self.stdscr, ['Not logged in'])
@BaseController.register('u')
@PageController.register('u')
def login(self):
"""
Prompt to log into the user's account, or log out of the current
@@ -362,138 +165,105 @@ class BasePage(object):
"""
if self.reddit.is_oauth_session():
ch = prompt_input(self.stdscr, "Log out? (y/n): ")
if ch == 'y':
if self.term.prompt_y_or_n('Log out? (y/n): '):
self.oauth.clear_oauth_data()
show_notification(self.stdscr, ['Logged out'])
elif ch != 'n':
curses.flash()
self.term.show_notification('Logged out')
else:
self.oauth.authorize()
@BaseController.register('d')
def delete(self):
@PageController.register('d')
@logged_in
def delete_item(self):
"""
Delete a submission or comment.
"""
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
return
data = self.content.get(self.nav.absolute_index)
if data.get('author') != self.reddit.user.name:
curses.flash()
self.term.flash()
return
prompt = 'Are you sure you want to delete this? (y/n): '
char = prompt_input(self.stdscr, prompt)
if char != 'y':
show_notification(self.stdscr, ['Aborted'])
if not self.term.prompt_y_or_n(prompt):
self.term.show_notification('Aborted')
return
with self.safe_call as s:
with self.loader(message='Deleting', delay=0):
with self.term.loader(message='Deleting', delay=0):
data['object'].delete()
# Give reddit time to process the request
time.sleep(2.0)
s.catch = False
if self.term.loader.exception is None:
self.refresh_content()
@BaseController.register('e')
@PageController.register('e')
@logged_in
def edit(self):
"""
Edit a submission or comment.
"""
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
return
data = self.content.get(self.nav.absolute_index)
if data.get('author') != self.reddit.user.name:
curses.flash()
self.term.flash()
return
if data['type'] == 'Submission':
subreddit = self.reddit.get_subreddit(self.content.name)
content = data['text']
info = SUBMISSION_FILE.format(content=content, name=subreddit)
info = docs.SUBMISSION_EDIT_FILE.format(
content=content, name=subreddit)
elif data['type'] == 'Comment':
content = data['body']
info = COMMENT_EDIT_FILE.format(content=content)
info = docs.COMMENT_EDIT_FILE.format(content=content)
else:
curses.flash()
self.term.flash()
return
text = open_editor(info)
text = self.term.open_editor(info)
if text == content:
show_notification(self.stdscr, ['Aborted'])
self.term.show_notification('Aborted')
return
with self.safe_call as s:
with self.loader(message='Editing', delay=0):
with self.term.loader(message='Editing', delay=0):
data['object'].edit(text)
time.sleep(2.0)
s.catch = False
if self.term.loader.exception is None:
self.refresh_content()
@BaseController.register('i')
@PageController.register('i')
@logged_in
def get_inbox(self):
"""
Checks the inbox for unread messages and displays a notification.
"""
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
return
inbox = len(list(self.reddit.get_unread(limit=1)))
try:
if inbox > 0:
show_notification(self.stdscr, ['New Messages'])
elif inbox == 0:
show_notification(self.stdscr, ['No New Messages'])
except praw.errors.LoginOrScopeRequired:
show_notification(self.stdscr, ['Not Logged In'])
message = 'New Messages' if inbox > 0 else 'No New Messages'
self.term.show_notification(message)
def clear_input_queue(self):
"""
Clear excessive input caused by the scroll wheel or holding down a key
"""
self.stdscr.nodelay(1)
while self.stdscr.getch() != -1:
with self.term.no_delay():
while self.term.getch() != -1:
continue
self.stdscr.nodelay(0)
@property
def safe_call(self):
"""
Wrap praw calls with extended error handling.
If a PRAW related error occurs inside of this context manager, a
notification will be displayed on the screen instead of the entire
application shutting down. This function will return a callback that
can be used to check the status of the call.
Usage:
#>>> with self.safe_call as s:
#>>> self.reddit.submit(...)
#>>> s.catch = False
#>>> on_success()
"""
return SafeCaller(self.stdscr)
def draw(self):
n_rows, n_cols = self.stdscr.getmaxyx()
if n_rows < self.MIN_HEIGHT or n_cols < self.MIN_WIDTH:
window = self.term.stdscr
n_rows, n_cols = window.getmaxyx()
if n_rows < self.term.MIN_HEIGHT or n_cols < self.term.MIN_WIDTH:
# TODO: Will crash when you try to navigate if the terminal is too
# small at startup because self._subwindows will never be populated
return
# Note: 2 argument form of derwin breaks PDcurses on Windows 7!
self._header_window = self.stdscr.derwin(1, n_cols, 0, 0)
self._content_window = self.stdscr.derwin(n_rows - 1, n_cols, 1, 0)
self._header_window = window.derwin(1, n_cols, 0, 0)
self._content_window = window.derwin(n_rows - 1, n_cols, 1, 0)
self.stdscr.erase()
window.erase()
self._draw_header()
self._draw_content()
self._add_cursor()
@@ -503,20 +273,26 @@ class BasePage(object):
n_rows, n_cols = self._header_window.getmaxyx()
self._header_window.erase()
attr = curses.A_REVERSE | curses.A_BOLD | Color.CYAN
self._header_window.bkgd(' ', attr)
# curses.bkgd expects bytes in py2 and unicode in py3
ch, attr = str(' '), curses.A_REVERSE | curses.A_BOLD | Color.CYAN
self._header_window.bkgd(ch, attr)
sub_name = self.content.name.replace('/r/front', 'Front Page')
add_line(self._header_window, sub_name, 0, 0)
self.term.add_line(self._header_window, sub_name, 0, 0)
if self.content.order is not None:
add_line(self._header_window, ' [{}]'.format(self.content.order))
order = ' [{}]'.format(self.content.order)
self.term.add_line(self._header_window, order)
if self.reddit.user is not None:
# The starting position of the name depends on if we're converting
# to ascii or not
width = len if self.config['ascii'] else textual_width
username = self.reddit.user.name
s_col = (n_cols - textual_width(username) - 1)
s_col = (n_cols - width(username) - 1)
# Only print username if it fits in the empty space on the right
if (s_col - 1) >= textual_width(sub_name):
add_line(self._header_window, username, 0, s_col)
if (s_col - 1) >= width(sub_name):
self.term.add_line(self._header_window, username, 0, s_col)
self._header_window.refresh()
@@ -543,7 +319,7 @@ class BasePage(object):
start = current_row - window_rows if inverted else current_row
subwindow = self._content_window.derwin(
window_rows, window_cols, start, data['offset'])
attr = self.draw_item(subwindow, data, inverted)
attr = self._draw_item(subwindow, data, inverted)
self._subwindows.append((subwindow, attr))
available_rows -= (window_rows + 1) # Add one for the blank line
current_row += step * (window_rows + 1)
@@ -571,7 +347,7 @@ class BasePage(object):
self._remove_cursor()
valid, redraw = self.nav.move(direction, len(self._subwindows))
if not valid:
curses.flash()
self.term.flash()
# Note: ACS_VLINE doesn't like changing the attribute,
# so always redraw.
@@ -582,14 +358,14 @@ class BasePage(object):
self._remove_cursor()
valid, redraw = self.nav.move_page(direction, len(self._subwindows)-1)
if not valid:
curses.flash()
self.term.flash()
# Note: ACS_VLINE doesn't like changing the attribute,
# so always redraw.
self._draw_content()
self._add_cursor()
def _edit_cursor(self, attribute=None):
def _edit_cursor(self, attribute):
# Don't allow the cursor to go below page index 0
if self.nav.absolute_index < 0:
@@ -599,7 +375,7 @@ class BasePage(object):
# This could happen if the window is resized and the cursor index is
# pushed out of bounds
if self.nav.cursor_index >= len(self._subwindows):
self.nav.cursor_index = len(self._subwindows)-1
self.nav.cursor_index = len(self._subwindows) - 1
window, attr = self._subwindows[self.nav.cursor_index]
if attr is not None:

View File

@@ -1,47 +1,33 @@
import curses
import sys
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import logging
import curses
from . import docs
from .content import SubmissionContent
from .page import BasePage, Navigator, BaseController
from .helpers import open_browser, open_editor
from .curses_helpers import (Color, LoadScreen, get_arrow, get_gold, add_line,
show_notification)
from .docs import COMMENT_FILE
__all__ = ['SubmissionController', 'SubmissionPage']
_logger = logging.getLogger(__name__)
from .page import Page, PageController, logged_in
from .objects import Navigator, Color
from .terminal import Terminal
class SubmissionController(BaseController):
class SubmissionController(PageController):
character_map = {}
class SubmissionPage(BasePage):
class SubmissionPage(Page):
def __init__(self, stdscr, reddit, oauth, url=None, submission=None):
def __init__(self, reddit, term, config, oauth, url=None, submission=None):
super(SubmissionPage, self).__init__(reddit, term, config, oauth)
if url:
self.content = SubmissionContent.from_url(reddit, url, term.loader)
else:
self.content = SubmissionContent(submission, term.loader)
self.controller = SubmissionController(self)
self.loader = LoadScreen(stdscr)
if url:
content = SubmissionContent.from_url(reddit, url, self.loader)
elif submission:
content = SubmissionContent(submission, self.loader)
else:
raise ValueError('Must specify url or submission')
super(SubmissionPage, self).__init__(stdscr, reddit, content, oauth,
page_index=-1)
def loop(self):
"Main control loop"
self.active = True
while self.active:
self.draw()
cmd = self.stdscr.getch()
self.controller.trigger(cmd)
# Start at the submission post, which is indexed as -1
self.nav = Navigator(self.content.get, page_index=-1)
@SubmissionController.register(curses.KEY_RIGHT, 'l', ' ')
def toggle_comment(self):
@@ -50,9 +36,9 @@ class SubmissionPage(BasePage):
current_index = self.nav.absolute_index
self.content.toggle(current_index)
if self.nav.inverted:
# Reset the page so that the bottom is at the cursor position.
# This is a workaround to handle if folding the causes the
# cursor index to go out of bounds.
# Reset the navigator so that the cursor is at the bottom of the
# page. This is a workaround to handle if folding the comment
# causes the cursor index to go out of bounds.
self.nav.page_index, self.nav.cursor_index = current_index, 0
@SubmissionController.register(curses.KEY_LEFT, 'h')
@@ -63,88 +49,93 @@ class SubmissionPage(BasePage):
@SubmissionController.register(curses.KEY_F5, 'r')
def refresh_content(self, order=None):
"Re-download comments reset the page index"
"Re-download comments and reset the page index"
order = order or self.content.order
url = self.content.name
with self.term.loader():
self.content = SubmissionContent.from_url(
self.reddit, self.content.name, self.loader, order=order)
self.reddit, url, self.term.loader, order=order)
if not self.term.loader.exception:
self.nav = Navigator(self.content.get, page_index=-1)
@SubmissionController.register(curses.KEY_ENTER, 10, 'o')
@SubmissionController.register(curses.KEY_ENTER, Terminal.RETURN, 'o')
def open_link(self):
"Open the current submission page with the webbrowser"
"Open the selected item with the webbrowser"
data = self.content.get(self.nav.absolute_index)
url = data.get('permalink')
if url:
open_browser(url)
self.term.open_browser(url)
else:
curses.flash()
self.term.flash()
@SubmissionController.register('c')
@logged_in
def add_comment(self):
"""
Add a top-level comment if the submission is selected, or reply to the
selected comment.
"""
Submit a reply to the selected item.
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
return
Selected item:
Submission - add a top level comment
Comment - add a comment reply
"""
data = self.content.get(self.nav.absolute_index)
if data['type'] == 'Submission':
content = data['text']
body = data['text']
reply = data['object'].add_comment
elif data['type'] == 'Comment':
content = data['body']
body = data['body']
reply = data['object'].reply
else:
curses.flash()
self.term.flash()
return
# Comment out every line of the content
content = '\n'.join(['# |' + line for line in content.split('\n')])
comment_info = COMMENT_FILE.format(
# Construct the text that will be displayed in the editor file.
# The post body will be commented out and added for reference
lines = ['# |' + line for line in body.split('\n')]
content = '\n'.join(lines)
comment_info = docs.COMMENT_FILE.format(
author=data['author'],
type=data['type'].lower(),
content=content)
comment_text = open_editor(comment_info)
if not comment_text:
show_notification(self.stdscr, ['Aborted'])
comment = self.term.open_editor(comment_info)
if not comment:
self.term.show_notification('Aborted')
return
with self.safe_call as s:
with self.loader(message='Posting', delay=0):
if data['type'] == 'Submission':
data['object'].add_comment(comment_text)
else:
data['object'].reply(comment_text)
with self.term.loader(message='Posting', delay=0):
reply(comment)
# Give reddit time to process the submission
time.sleep(2.0)
s.catch = False
if not self.term.loader.exception:
self.refresh_content()
@SubmissionController.register('d')
@logged_in
def delete_comment(self):
"Delete a comment as long as it is not the current submission"
if self.nav.absolute_index != -1:
self.delete()
self.delete_item()
else:
curses.flash()
self.term.flash()
def draw_item(self, win, data, inverted=False):
def _draw_item(self, win, data, inverted=False):
if data['type'] == 'MoreComments':
return self.draw_more_comments(win, data)
return self._draw_more_comments(win, data)
elif data['type'] == 'HiddenComment':
return self.draw_more_comments(win, data)
return self._draw_more_comments(win, data)
elif data['type'] == 'Comment':
return self.draw_comment(win, data, inverted=inverted)
return self._draw_comment(win, data, inverted=inverted)
else:
return self.draw_submission(win, data)
return self._draw_submission(win, data)
@staticmethod
def draw_comment(win, data, inverted=False):
def _draw_comment(self, win, data, inverted=False):
n_rows, n_cols = win.getmaxyx()
n_cols -= 1
@@ -158,73 +149,65 @@ class SubmissionPage(BasePage):
attr = curses.A_BOLD
attr |= (Color.BLUE if not data['is_author'] else Color.GREEN)
add_line(win, u'{author} '.format(**data), row, 1, attr)
self.term.add_line(win, '{author} '.format(**data), row, 1, attr)
if data['flair']:
attr = curses.A_BOLD | Color.YELLOW
add_line(win, u'{flair} '.format(**data), attr=attr)
self.term.add_line(win, '{flair} '.format(**data), attr=attr)
text, attr = get_arrow(data['likes'])
add_line(win, text, attr=attr)
add_line(win, u' {score} {created} '.format(**data))
text, attr = self.term.get_arrow(data['likes'])
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {score} {created} '.format(**data))
if data['gold']:
text, attr = get_gold()
add_line(win, text, attr=attr)
text, attr = self.term.guilded
self.term.add_line(win, text, attr=attr)
for row, text in enumerate(data['split_body'], start=offset + 1):
for row, text in enumerate(data['split_body'], start=offset+1):
if row in valid_rows:
add_line(win, text, row, 1)
self.term.add_line(win, text, row, 1)
# Unfortunately vline() doesn't support custom color so we have to
# build it one segment at a time.
attr = Color.get_level(data['level'])
for y in range(n_rows):
x = 0
# http://bugs.python.org/issue21088
if (sys.version_info.major,
sys.version_info.minor,
sys.version_info.micro) == (3, 4, 0):
x, y = y, x
for y in range(n_rows):
self.term.addch(win, y, x, curses.ACS_VLINE, attr)
win.addch(y, x, curses.ACS_VLINE, attr)
return attr | curses.ACS_VLINE
return (attr | curses.ACS_VLINE)
@staticmethod
def draw_more_comments(win, data):
def _draw_more_comments(self, win, data):
n_rows, n_cols = win.getmaxyx()
n_cols -= 1
add_line(win, u'{body}'.format(**data), 0, 1)
add_line(win, u' [{count}]'.format(**data), attr=curses.A_BOLD)
self.term.add_line(win, '{body}'.format(**data), 0, 1)
self.term.add_line(win, ' [{count}]'.format(**data), attr=curses.A_BOLD)
attr = Color.get_level(data['level'])
win.addch(0, 0, curses.ACS_VLINE, attr)
self.term.addch(win, 0, 0, curses.ACS_VLINE, attr)
return (attr | curses.ACS_VLINE)
return attr | curses.ACS_VLINE
@staticmethod
def draw_submission(win, data):
def _draw_submission(self, win, data):
n_rows, n_cols = win.getmaxyx()
n_cols -= 3 # one for each side of the border + one for offset
for row, text in enumerate(data['split_title'], start=1):
add_line(win, text, row, 1, curses.A_BOLD)
self.term.add_line(win, text, row, 1, curses.A_BOLD)
row = len(data['split_title']) + 1
attr = curses.A_BOLD | Color.GREEN
add_line(win, u'{author}'.format(**data), row, 1, attr)
self.term.add_line(win, '{author}'.format(**data), row, 1, attr)
attr = curses.A_BOLD | Color.YELLOW
if data['flair']:
add_line(win, u' {flair}'.format(**data), attr=attr)
add_line(win, u' {created} {subreddit}'.format(**data))
self.term.add_line(win, ' {flair}'.format(**data), attr=attr)
self.term.add_line(win, ' {created} {subreddit}'.format(**data))
row = len(data['split_title']) + 2
attr = curses.A_UNDERLINE | Color.BLUE
add_line(win, u'{url}'.format(**data), row, 1, attr)
self.term.add_line(win, '{url}'.format(**data), row, 1, attr)
offset = len(data['split_title']) + 3
# Cut off text if there is not enough room to display the whole post
@@ -235,20 +218,20 @@ class SubmissionPage(BasePage):
split_text.append('(Not enough space to display)')
for row, text in enumerate(split_text, start=offset):
add_line(win, text, row, 1)
self.term.add_line(win, text, row, 1)
row = len(data['split_title']) + len(split_text) + 3
add_line(win, u'{score} '.format(**data), row, 1)
text, attr = get_arrow(data['likes'])
add_line(win, text, attr=attr)
add_line(win, u' {comments} '.format(**data))
self.term.add_line(win, '{score} '.format(**data), row, 1)
text, attr = self.term.get_arrow(data['likes'])
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {comments} '.format(**data))
if data['gold']:
text, attr = get_gold()
add_line(win, text, attr=attr)
text, attr = self.term.gold
self.term.add_line(win, text, attr=attr)
if data['nsfw']:
text, attr = 'NSFW', (curses.A_BOLD | Color.RED)
add_line(win, text, attr=attr)
self.term.add_line(win, text, attr=attr)
win.border()

View File

@@ -1,54 +1,40 @@
import curses
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import logging
import atexit
import curses
import requests
import six
from .exceptions import SubredditError, AccountError
from .page import BasePage, Navigator, BaseController
from . import docs
from .content import SubredditContent
from .page import Page, PageController, logged_in
from .objects import Navigator, Color
from .submission import SubmissionPage
from .subscription import SubscriptionPage
from .content import SubredditContent
from .helpers import open_browser, open_editor
from .docs import SUBMISSION_FILE
from .history import load_history, save_history
from .curses_helpers import (Color, LoadScreen, add_line, get_arrow, get_gold,
show_notification, prompt_input)
__all__ = ['history', 'SubredditController', 'SubredditPage']
_logger = logging.getLogger(__name__)
history = load_history()
from .terminal import Terminal
@atexit.register
def save_links():
global history
save_history(history)
class SubredditController(BaseController):
class SubredditController(PageController):
character_map = {}
class SubredditPage(BasePage):
class SubredditPage(Page):
def __init__(self, stdscr, reddit, oauth, name):
def __init__(self, reddit, term, config, oauth, name, url=None):
"""
Params:
name (string): Name of subreddit to open
url (string): Optional submission to load upon start
"""
super(SubredditPage, self).__init__(reddit, term, config, oauth)
self.content = SubredditContent.from_name(reddit, name, term.loader)
self.controller = SubredditController(self)
self.loader = LoadScreen(stdscr)
self.oauth = oauth
self.nav = Navigator(self.content.get)
content = SubredditContent.from_name(reddit, name, self.loader)
super(SubredditPage, self).__init__(stdscr, reddit, content, oauth)
def loop(self):
"Main control loop"
while True:
self.draw()
cmd = self.stdscr.getch()
self.controller.trigger(cmd)
if url:
self.open_submission(url=url)
@SubredditController.register(curses.KEY_F5, 'r')
def refresh_content(self, name=None, order=None):
@@ -62,16 +48,10 @@ class SubredditPage(BasePage):
if order == 'ignore':
order = None
try:
with self.term.loader():
self.content = SubredditContent.from_name(
self.reddit, name, self.loader, order=order)
except AccountError:
show_notification(self.stdscr, ['Not logged in'])
except SubredditError:
show_notification(self.stdscr, ['Invalid subreddit'])
except requests.HTTPError:
show_notification(self.stdscr, ['Could not reach subreddit'])
else:
self.reddit, name, self.term.loader, order=order)
if not self.term.loader.exception:
self.nav = Navigator(self.content.get)
@SubredditController.register('f')
@@ -79,115 +59,114 @@ class SubredditPage(BasePage):
"Open a prompt to search the given subreddit"
name = name or self.content.name
prompt = 'Search {}:'.format(name)
query = prompt_input(self.stdscr, prompt)
if query is None:
query = self.term.prompt_input('Search {0}:'.format(name))
if not query:
return
try:
with self.term.loader():
self.content = SubredditContent.from_name(
self.reddit, name, self.loader, query=query)
except (IndexError, SubredditError): # if there are no submissions
show_notification(self.stdscr, ['No results found'])
else:
self.reddit, name, self.term.loader, query=query)
if not self.term.loader.exception:
self.nav = Navigator(self.content.get)
@SubredditController.register('/')
def prompt_subreddit(self):
"Open a prompt to navigate to a different subreddit"
prompt = 'Enter Subreddit: /r/'
name = prompt_input(self.stdscr, prompt)
name = self.term.prompt_input('Enter Subreddit: /r/')
if name is not None:
self.refresh_content(name=name, order='ignore')
@SubredditController.register(curses.KEY_RIGHT, 'l')
def open_submission(self):
def open_submission(self, url=None):
"Select the current submission to view posts"
data = {}
if url is None:
data = self.content.get(self.nav.absolute_index)
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
url=data['permalink'])
page.loop()
if data['url_type'] == 'selfpost':
global history
history.add(data['url_full'])
url = data['permalink']
@SubredditController.register(curses.KEY_ENTER, 10, 'o')
with self.term.loader():
page = SubmissionPage(
self.reddit, self.term, self.config, self.oauth, url=url)
if self.term.loader.exception:
return
page.loop()
if data.get('url_type') in ('selfpost', 'x-post'):
self.config.history.add(data['url_full'])
@SubredditController.register(curses.KEY_ENTER, Terminal.RETURN, 'o')
def open_link(self):
"Open a link with the webbrowser"
data = self.content.get(self.nav.absolute_index)
url = data['url_full']
global history
history.add(url)
if data['url_type'] in ['x-post', 'selfpost']:
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
url=url)
page.loop()
data = self.content.get(self.nav.absolute_index)
if data['url_type'] in ('x-post', 'selfpost'):
# Open links to other posts directly in RTV
self.open_submission()
else:
open_browser(url)
self.term.open_browser(data['url_full'])
self.config.history.add(data['url_full'])
@SubredditController.register('c')
@logged_in
def post_submission(self):
"Post a new submission to the given subreddit"
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
# Check that the subreddit can be submitted to
name = self.content.name
if '+' in name or name in ('/r/all', '/r/front', '/r/me'):
self.term.show_notification("Can't post to {0}".format(name))
return
# Strips the subreddit to just the name
# Make sure it is a valid subreddit for submission
subreddit = self.reddit.get_subreddit(self.content.name)
sub = str(subreddit).split('/')[2]
if '+' in sub or sub in ('all', 'front', 'me'):
show_notification(self.stdscr, ['Invalid subreddit'])
submission_info = docs.SUBMISSION_FILE.format(name=name)
text = self.term.open_editor(submission_info)
if not text or '\n' not in text:
self.term.show_notification('Aborted')
return
# Open the submission window
submission_info = SUBMISSION_FILE.format(name=subreddit, content='')
curses.endwin()
submission_text = open_editor(submission_info)
curses.doupdate()
# Validate the submission content
if not submission_text:
show_notification(self.stdscr, ['Aborted'])
return
if '\n' not in submission_text:
show_notification(self.stdscr, ['No content'])
return
title, content = submission_text.split('\n', 1)
with self.safe_call as s:
with self.loader(message='Posting', delay=0):
post = self.reddit.submit(sub, title, text=content)
title, content = text.split('\n', 1)
with self.term.loader(message='Posting', delay=0):
submission = self.reddit.submit(name, title, text=content)
# Give reddit time to process the submission
time.sleep(2.0)
if self.term.loader.exception:
return
# Open the newly created post
s.catch = False
page = SubmissionPage(self.stdscr, self.reddit, self.oauth,
submission=post)
with self.term.loader():
page = SubmissionPage(
self.reddit, self.term, self.config, self.oauth,
submission=submission)
if self.term.loader.exception:
return
page.loop()
self.refresh_content()
@SubredditController.register('s')
@logged_in
def open_subscriptions(self):
"Open user subscriptions page"
if not self.reddit.is_oauth_session():
show_notification(self.stdscr, ['Not logged in'])
with self.term.loader():
page = SubscriptionPage(
self.reddit, self.term, self.config, self.oauth)
if self.term.loader.exception:
return
# Open subscriptions page
page = SubscriptionPage(self.stdscr, self.reddit, self.oauth)
page.loop()
# When user has chosen a subreddit in the subscriptions list,
# When the user has chosen a subreddit in the subscriptions list,
# refresh content with the selected subreddit
if page.selected_subreddit_data is not None:
self.refresh_content(name=page.selected_subreddit_data['name'])
if page.subreddit_data is not None:
self.refresh_content(name=page.subreddit_data['name'],
order='ignore')
@staticmethod
def draw_item(win, data, inverted=False):
def _draw_item(self, win, data, inverted=False):
n_rows, n_cols = win.getmaxyx()
n_cols -= 1 # Leave space for the cursor in the first column
@@ -199,33 +178,36 @@ class SubredditPage(BasePage):
n_title = len(data['split_title'])
for row, text in enumerate(data['split_title'], start=offset):
if row in valid_rows:
add_line(win, text, row, 1, curses.A_BOLD)
self.term.add_line(win, text, row, 1, curses.A_BOLD)
row = n_title + offset
if row in valid_rows:
seen = (data['url_full'] in history)
seen = (data['url_full'] in self.config.history)
link_color = Color.MAGENTA if seen else Color.BLUE
attr = curses.A_UNDERLINE | link_color
add_line(win, u'{url}'.format(**data), row, 1, attr)
self.term.add_line(win, '{url}'.format(**data), row, 1, attr)
row = n_title + offset + 1
if row in valid_rows:
add_line(win, u'{score} '.format(**data), row, 1)
text, attr = get_arrow(data['likes'])
add_line(win, text, attr=attr)
add_line(win, u' {created} {comments} '.format(**data))
self.term.add_line(win, '{score} '.format(**data), row, 1)
text, attr = self.term.get_arrow(data['likes'])
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {created} {comments} '.format(**data))
if data['gold']:
text, attr = get_gold()
add_line(win, text, attr=attr)
text, attr = self.term.guilded
self.term.add_line(win, text, attr=attr)
if data['nsfw']:
text, attr = 'NSFW', (curses.A_BOLD | Color.RED)
add_line(win, text, attr=attr)
self.term.add_line(win, text, attr=attr)
row = n_title + offset + 2
if row in valid_rows:
add_line(win, u'{author}'.format(**data), row, 1, curses.A_BOLD)
add_line(win, u' /r/{subreddit}'.format(**data), attr=Color.YELLOW)
text = '{author}'.format(**data)
self.term.add_line(win, text, row, 1, curses.A_BOLD)
text = ' /r/{subreddit}'.format(**data)
self.term.add_line(win, text, attr=Color.YELLOW)
if data['flair']:
add_line(win, u' {flair}'.format(**data), attr=Color.RED)
text = ' {flair}'.format(**data)
self.term.add_line(win, text, attr=Color.RED)

View File

@@ -1,66 +1,56 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import curses
import logging
from .page import Page, PageController
from .content import SubscriptionContent
from .page import BasePage, Navigator, BaseController
from .curses_helpers import (Color, LoadScreen, add_line)
__all__ = ['SubscriptionController', 'SubscriptionPage']
_logger = logging.getLogger(__name__)
from .objects import Color, Navigator
from .terminal import Terminal
class SubscriptionController(BaseController):
class SubscriptionController(PageController):
character_map = {}
class SubscriptionPage(BasePage):
class SubscriptionPage(Page):
def __init__(self, stdscr, reddit, oauth):
def __init__(self, reddit, term, config, oauth):
super(SubscriptionPage, self).__init__(reddit, term, config, oauth)
self.content = SubscriptionContent.from_user(reddit, term.loader)
self.controller = SubscriptionController(self)
self.loader = LoadScreen(stdscr)
self.selected_subreddit_data = None
content = SubscriptionContent.from_user(reddit, self.loader)
super(SubscriptionPage, self).__init__(stdscr, reddit, content, oauth)
def loop(self):
"Main control loop"
self.active = True
while self.active:
self.draw()
cmd = self.stdscr.getch()
self.controller.trigger(cmd)
self.nav = Navigator(self.content.get)
self.subreddit_data = None
@SubscriptionController.register(curses.KEY_F5, 'r')
def refresh_content(self, order=None):
"Re-download all subscriptions and reset the page index"
if order:
# reddit.get_my_subreddits() does not support sorting by order
curses.flash()
else:
if order:
self.term.flash()
return
self.content = SubscriptionContent.from_user(self.reddit,
self.loader)
self.term.loader)
self.nav = Navigator(self.content.get)
@SubscriptionController.register(curses.KEY_ENTER, 10, curses.KEY_RIGHT)
def store_selected_subreddit(self):
@SubscriptionController.register(curses.KEY_ENTER, Terminal.RETURN,
curses.KEY_RIGHT, 'l')
def select_subreddit(self):
"Store the selected subreddit and return to the subreddit page"
self.selected_subreddit_data = self.content.get(
self.nav.absolute_index)
self.subreddit_data = self.content.get(self.nav.absolute_index)
self.active = False
@SubscriptionController.register(curses.KEY_LEFT, 'h', 's')
@SubscriptionController.register(curses.KEY_LEFT, Terminal.ESCAPE, 'h', 's')
def close_subscriptions(self):
"Close subscriptions and return to the subreddit page"
self.active = False
@staticmethod
def draw_item(win, data, inverted=False):
def _draw_item(self, win, data, inverted=False):
n_rows, n_cols = win.getmaxyx()
n_cols -= 1 # Leave space for the cursor in the first column
@@ -71,9 +61,9 @@ class SubscriptionPage(BasePage):
row = offset
if row in valid_rows:
attr = curses.A_BOLD | Color.YELLOW
add_line(win, u'{name}'.format(**data), row, 1, attr)
self.term.add_line(win, '{name}'.format(**data), row, 1, attr)
row = offset + 1
for row, text in enumerate(data['split_title'], start=row):
if row in valid_rows:
add_line(win, text, row, 1)
self.term.add_line(win, text, row, 1)

View File

@@ -28,9 +28,10 @@
{% if error == 'access_denied' %}
<h1 style="color: red">Access Denied</h1><hr>
<p><span style="font-weight: bold">Reddit Terminal Viewer</span> was denied access and will continue to operate in unauthenticated mode, you can close this window.
{% elif error != 'placeholder' %}
<h1 style="color: red">Error : {{ error }}</h1>
{% elif (state == 'placeholder' or code == 'placeholder') %}
{% elif error is not None %}
<h1 style="color: red">Error</h1><hr>
<p>{{ error }}</p>
{% elif (state is None or code is None) %}
<h1>Wait...</h1><hr>
<p>This page is supposed to be a Reddit OAuth callback. You can't just come here hands in your pocket!</p>
{% else %}

446
rtv/terminal.py Normal file
View File

@@ -0,0 +1,446 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import sys
import time
import codecs
import curses
import webbrowser
import subprocess
import curses.ascii
from curses import textpad
from contextlib import contextmanager
from tempfile import NamedTemporaryFile
import six
from kitchen.text.display import textual_width_chop
from .objects import LoadScreen, Color
from .exceptions import EscapeInterrupt, ProgramError
class Terminal(object):
MIN_HEIGHT = 10
MIN_WIDTH = 20
# ASCII code
ESCAPE = 27
RETURN = 10
def __init__(self, stdscr, ascii=False):
self.stdscr = stdscr
self.ascii = ascii
self.loader = LoadScreen(self)
self._display = None
@property
def up_arrow(self):
symbol = '^' if self.ascii else ''
attr = curses.A_BOLD | Color.GREEN
return symbol, attr
@property
def down_arrow(self):
symbol = 'v' if self.ascii else ''
attr = curses.A_BOLD | Color.RED
return symbol, attr
@property
def neutral_arrow(self):
symbol = 'o' if self.ascii else ''
attr = curses.A_BOLD
return symbol, attr
@property
def guilded(self):
symbol = '*' if self.ascii else ''
attr = curses.A_BOLD | Color.YELLOW
return symbol, attr
@property
def display(self):
"""
Use a number of methods to guess if the default webbrowser will open in
the background as opposed to opening directly in the terminal.
"""
if self._display is None:
display = bool(os.environ.get("DISPLAY"))
# Use the convention defined here to parse $BROWSER
# https://docs.python.org/2/library/webbrowser.html
console_browsers = ['www-browser', 'links', 'links2', 'elinks',
'lynx', 'w3m']
if "BROWSER" in os.environ:
user_browser = os.environ["BROWSER"].split(os.pathsep)[0]
if user_browser in console_browsers:
display = False
if webbrowser._tryorder:
if webbrowser._tryorder[0] in console_browsers:
display = False
self._display = display
return self._display
@staticmethod
def flash():
return curses.flash()
@staticmethod
def addch(window, y, x, ch, attr):
"""
Curses addch() method that fixes a major bug in python 3.4.
See http://bugs.python.org/issue21088
"""
if sys.version_info[:3] == (3, 4, 0):
y, x = x, y
window.addch(y, x, ch, attr)
def getch(self):
return self.stdscr.getch()
@staticmethod
@contextmanager
def suspend():
"""
Suspend curses in order to open another subprocess in the terminal.
"""
try:
curses.endwin()
yield
finally:
curses.doupdate()
@contextmanager
def no_delay(self):
"""
Temporarily turn off character delay mode. In this mode, getch will not
block while waiting for input and will return -1 if no key has been
pressed.
"""
try:
self.stdscr.nodelay(1)
yield
finally:
self.stdscr.nodelay(0)
def get_arrow(self, likes):
"""
Curses does define constants for symbols (e.g. curses.ACS_BULLET).
However, they rely on using the curses.addch() function, which has been
found to be buggy and a general PITA to work with. By defining them as
unicode points they can be added via the more reliable curses.addstr().
http://bugs.python.org/issue21088
"""
if likes is None:
return self.neutral_arrow
elif likes:
return self.up_arrow
else:
return self.down_arrow
def clean(self, string, n_cols=None):
"""
Required reading!
http://nedbatchelder.com/text/unipain.html
Python 2 input string will be a unicode type (unicode code points).
Curses will accept unicode if all of the points are in the ascii range.
However, if any of the code points are not valid ascii curses will
throw a UnicodeEncodeError: 'ascii' codec can't encode character,
ordinal not in range(128). If we encode the unicode to a utf-8 byte
string and pass that to curses, it will render correctly.
Python 3 input string will be a string type (unicode code points).
Curses will accept that in all cases. However, the n character count in
addnstr will not be correct. If code points are passed to addnstr,
curses will treat each code point as one character and will not account
for wide characters. If utf-8 is passed in, addnstr will treat each
'byte' as a single character.
"""
if n_cols is not None and n_cols <= 0:
return ''
if self.ascii:
if isinstance(string, six.binary_type):
string = string.decode('utf-8')
string = string.encode('ascii', 'replace')
return string[:n_cols] if n_cols else string
else:
if n_cols:
string = textual_width_chop(string, n_cols)
if isinstance(string, six.text_type):
string = string.encode('utf-8')
return string
def add_line(self, window, text, row=None, col=None, attr=None):
"""
Unicode aware version of curses's built-in addnstr method.
Safely draws a line of text on the window starting at position
(row, col). Checks the boundaries of the window and cuts off the text
if it exceeds the length of the window.
"""
# The following arg combos must be supported to conform with addnstr
# (window, text)
# (window, text, attr)
# (window, text, row, col)
# (window, text, row, col, attr)
cursor_row, cursor_col = window.getyx()
row = row if row is not None else cursor_row
col = col if col is not None else cursor_col
max_rows, max_cols = window.getmaxyx()
n_cols = max_cols - col - 1
if n_cols <= 0:
# Trying to draw outside of the screen bounds
return
text = self.clean(text, n_cols)
params = [] if attr is None else [attr]
window.addstr(row, col, text, *params)
def show_notification(self, message, timeout=None):
"""
Overlay a message box on the center of the screen and wait for input.
Params:
message (list or string): List of strings, one per line.
timeout (float): Optional, maximum length of time that the message
will be shown before disappearing.
"""
if isinstance(message, six.string_types):
message = [message]
n_rows, n_cols = self.stdscr.getmaxyx()
box_width = max(map(len, message)) + 2
box_height = len(message) + 2
# Cut off the lines of the message that don't fit on the screen
box_width = min(box_width, n_cols)
box_height = min(box_height, n_rows)
message = message[:box_height-2]
s_row = (n_rows - box_height) // 2
s_col = (n_cols - box_width) // 2
window = curses.newwin(box_height, box_width, s_row, s_col)
window.erase()
window.border()
for index, line in enumerate(message, start=1):
self.add_line(window, line, index, 1)
window.refresh()
ch, start = -1, time.time()
with self.no_delay():
while timeout is None or time.time() - start < timeout:
ch = self.getch()
if ch != -1:
break
time.sleep(0.01)
window.clear()
del window
self.stdscr.touchwin()
self.stdscr.refresh()
return ch
def open_browser(self, url):
"""
Open the given url using the default webbrowser. The preferred browser
can specified with the $BROWSER environment variable. If not specified,
python webbrowser will try to determine the default to use based on
your system.
For browsers requiring an X display, we call
webbrowser.open_new_tab(url) and redirect stdout/stderr to devnull.
This is a workaround to stop firefox from spewing warning messages to
the console. See http://bugs.python.org/issue22277 for a better
description of the problem.
For console browsers (e.g. w3m), RTV will suspend and display the
browser window within the same terminal. This mode is triggered either
when
1. $BROWSER is set to a known console browser, or
2. $DISPLAY is undefined, indicating that the terminal is running
headless
There may be other cases where console browsers are opened (xdg-open?)
but are not detected here.
"""
if self.display:
command = "import webbrowser; webbrowser.open_new_tab('%s')" % url
args = [sys.executable, '-c', command]
with open(os.devnull, 'ab+', 0) as null:
subprocess.check_call(args, stdout=null, stderr=null)
else:
with self.suspend():
webbrowser.open_new_tab(url)
def open_editor(self, data=''):
"""
Open a temporary file using the system's default editor.
The data string will be written to the file before opening. This
function will block until the editor has closed. At that point the file
will be read and and lines starting with '#' will be stripped.
"""
with NamedTemporaryFile(prefix='rtv-', suffix='.txt', mode='wb') as fp:
fp.write(codecs.encode(data, 'utf-8'))
fp.flush()
editor = os.getenv('RTV_EDITOR') or os.getenv('EDITOR') or 'nano'
try:
with self.suspend():
subprocess.Popen([editor, fp.name]).wait()
except OSError:
raise ProgramError('Could not open file with %s' % editor)
# Open a second file object to read. This appears to be necessary
# in order to read the changes made by some editors (gedit). w+
# mode does not work!
with codecs.open(fp.name, 'r', 'utf-8') as fp2:
text = ''.join(line for line in fp2 if not line.startswith('#'))
text = text.rstrip()
return text
def text_input(self, window, allow_resize=False):
"""
Transform a window into a text box that will accept user input and loop
until an escape sequence is entered.
If the escape key (27) is pressed, cancel the textbox and return None.
Otherwise, the textbox will wait until it is full (^j, or a new line is
entered on the bottom line) or the BEL key (^g) is pressed.
"""
window.clear()
# Set cursor mode to 1 because 2 doesn't display on some terminals
curses.curs_set(1)
# Keep insert_mode off to avoid the recursion error described here
# http://bugs.python.org/issue13051
textbox = textpad.Textbox(window)
textbox.stripspaces = 0
def validate(ch):
"Filters characters for special key sequences"
if ch == self.ESCAPE:
raise EscapeInterrupt()
if (not allow_resize) and (ch == curses.KEY_RESIZE):
raise EscapeInterrupt()
# Fix backspace for iterm
if ch == curses.ascii.DEL:
ch = curses.KEY_BACKSPACE
return ch
# Wrapping in an exception block so that we can distinguish when the
# user hits the return character from when the user tries to back out
# of the input.
try:
out = textbox.edit(validate=validate)
if isinstance(out, six.binary_type):
out = out.decode('utf-8')
except EscapeInterrupt:
out = None
curses.curs_set(0)
return self.strip_textpad(out)
def prompt_input(self, prompt, key=False):
"""
Display a text prompt at the bottom of the screen.
Params:
prompt (string): Text prompt that will be displayed
key (bool): If true, grab a single keystroke instead of a full
string. This can be faster than pressing enter for
single key prompts (e.g. y/n?)
"""
n_rows, n_cols = self.stdscr.getmaxyx()
attr = curses.A_BOLD | Color.CYAN
prompt = self.clean(prompt, n_cols - 1)
window = self.stdscr.derwin(
1, n_cols - len(prompt), n_rows - 1, len(prompt))
window.attrset(attr)
self.add_line(self.stdscr, prompt, n_rows-1, 0, attr)
self.stdscr.refresh()
if key:
curses.curs_set(1)
ch = self.getch()
# We can't convert the character to unicode, because it may return
# Invalid values for keys that don't map to unicode characters,
# e.g. F1
text = ch if ch != self.ESCAPE else None
curses.curs_set(0)
else:
text = self.text_input(window)
return text
def prompt_y_or_n(self, prompt):
"""
Wrapper around prompt_input for simple yes/no queries.
"""
ch = self.prompt_input(prompt, key=True)
if ch in (ord('Y'), ord('y')):
return True
elif ch in (ord('N'), ord('n'), None):
return False
else:
self.flash()
return False
@staticmethod
def strip_textpad(text):
"""
Attempt to intelligently strip excess whitespace from the output of a
curses textpad.
"""
if text is None:
return text
# Trivial case where the textbox is only one line long.
if '\n' not in text:
return text.rstrip()
# Allow one space at the end of the line. If there is more than one
# space, assume that a newline operation was intended by the user
stack, current_line = [], ''
for line in text.split('\n'):
if line.endswith(' '):
stack.append(current_line + line.rstrip())
current_line = ''
else:
current_line += line
stack.append(current_line)
# Prune empty lines at the bottom of the textbox.
for item in stack[::-1]:
if len(item) == 0:
stack.pop()
else:
break
out = '\n'.join(stack)
return out

View File

@@ -17,10 +17,12 @@ sys.path.insert(0, ROOT)
import rtv
from rtv import config
def main():
parser = config.build_parser()
help = parser.format_help()
help_sections = help.split('\n\n')
help_text = parser.format_help()
help_sections = help_text.split('\n\n')
data = {}
print('Fetching version')
@@ -53,7 +55,7 @@ def main():
print('Fetching copyright')
data['copyright'] = rtv.__copyright__
# Escape dashes is all of the sections
data = {k:v.replace('-', r'\-') for k,v in data.items()}
data = {k: v.replace('-', r'\-') for k, v in data.items()}
print('Reading from %s/rtv/templates/rtv.1.template' % ROOT)
with open(os.path.join(ROOT, 'rtv/templates/rtv.1.template')) as fp:
template = fp.read()

View File

@@ -33,8 +33,7 @@ setuptools.setup(
'Operating System :: POSIX',
'Natural Language :: English',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Topic :: Terminals',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary',

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/2xmo63/.json
response:
body:
string: !!binary |
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
headers:
CF-Cache-Status: [EXPIRED]
CF-RAY: [24e5671acf611ea7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7630']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:43:46 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dccd1bc55916440c54278def93fe461771449042226; expires=Thu,
01-Dec-16 07:43:46 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=4WAxfC8ixqjQvbb1X0%2BtFnNNghwyzsGSuyn6k3LYoBag9KyS4r1aE7zEdjNl2NW0xG4FZkQUvAg%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/.json?limit=5
response:
body:
string: !!binary |
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
PtmwQIZTG1WjPn78L15ilgJBPAAA
headers:
CF-RAY: [24e5678a0ffa11cb-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['3555']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:04 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d59c0c7619ceea234dfd9ba30d14233cd1449042244; expires=Thu,
01-Dec-16 07:44:04 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=ppw10JokN%2F9DEfw0kHvlao9XX3HbNGIeOM0qSLUI2dYFrpGys5ppXWY6XhqrWAIQYk2FyqsY%2BNA%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/python/top.json?limit=1024
response:
body:
string: !!binary |
H4sIAD+hXlYC/+1dDXPbNtL+Kzi/N+ckY337S+ncZHpJ27ht0rx1rr13mhsWIiEJNkkw/LCidPrf
330WoETJlqLIsmynvrlpLBIEdheLZxeLBfDHzrmOg52nYudHneU6HuzsiZ1A5pIe/bETmWAosyFe
47k/1GGQqph+//bH5MO8M/NNYCKpUWQnU2G//macD02MEj0ZxyrwemN6FxdhSI8iFWjpqainUNMf
f9KjrOilKgh0jhqmH6OuXH3IvWEehXj1jzD/6m+1mjh97v307beiVvvHIP8KDwN9IfxQZtk/3+1E
wbud8nlS/pHlqYkH+JWHXwXpUzxrTB+K3IhEmSRUIh/KXARGBOpChSaJVJwLE4tfiXEzyoSMA3qV
hGaMb37UcfHhqfgV38hUibEp0pkvM5UXSUYtSf88eyZemhHVrTJXUI5ruanRP2Jk0nMRGkP/0efq
GZPHxL+L38X4NUwb1Z8TxlSEv0ikueXJ/hZvtE9dNSByTT5UqTgzPUG9KnwTKRAeETtChqmSwbgu
/lXkAsUUMxHJeCzsZ33pqzyzMsErqk3omH7rTCQmy+viOxWrVIZEdl+JLC/6/br4qRfqgcy1ibM9
Ykrin76MdDjeE/V6XZyQDOJ/yCj56n863a9QY66IHCIrl+dUS+EPhRRBKkk5fZGaIlfirMhyMVY5
vpZhZkiy6Cuig7iSwh/KMFTxQNXFCyt+cK+n/TaVVUQ1VUXVoxZJKkPqmp6SqeyFVMn/qZwaGsoL
aiTVKrBCyXWkiBfogAxHcuzk2aPOnWgDCJwyNNBUg84n/ZANSYJEcUnWpE8kCYoopkqtgJVIiH3R
ahLTMs3qq2nEtzrN8j1oF1oqwkCcx8QX99+J8IuUBnIeOnXrjcXp6UsrJyJVzihuz3ywuk4vwlBE
7hui7UJHe6LI8F0eFbZUMs4VUUu1sMj6IH0qpdqFzjSJVRSxzmtcUn1QfsEacgVn/OPUkNwmSpKx
6k07ERxVOzHQ/b72i5BpGFDnMbWkZtSXbyeajX4mceOdSkn4ZkQcp+WQr6qk7Xn0QmqoYq1inzuG
mEcLNJ7HeBUUfo4exqBlbVzEzQlJEHBGdKFXQAgrk/uSND8dW/JMnyoymZqMQLSM8W1SV/q0oPGF
kqk4efGNe5iMaQikkR3LEkMWdcRKsRqSjkOv8ScNq5EYpDIZaj9rjHQwwAg/cYTRByzCtGAljEgr
fep/wginKiOdD8V/an2TjmQasPJkdnAmZAH6BPHER2hGdVLxHMOYK7BDA0q1x9Ku9Ks5lwQLvQIo
6ysxUk72JOSh7jMxsRrR+CNDFCswRg2ONOlkT/HIiQz17KT/F3XAKYpSj5EUIrDLjeiAVF33Mb6n
EL/HqkIMkpISmWwSQFQchGCX+qev6WGi0khnmQU5orBGb0nDhiqWMaEfnmIERIaHQmzG9DEAcL4y
a+5IsdK8kARhF/ShjknBwrJEQvAiBwpck0Sf18giqpgb5hZGJE8aHIsYp55lddCxNQg9glPxiyQN
gGHrixfGp8Fge8CX1THwvtAEvH1YCtbXIb4nkZNo8lKbikwB8Lj6Sa0TUPNDUkLin+C6L9O6a4vf
u7GF7rDjj0CSlC4D79QtpDjkmIzFL6/28HXoqLfK1DdhgPGLYTPEgI5z0j2qWAMm4hoh9oWWoTU1
3NEADzIeQHGSpEVmxW0CC6jmRdKzBtuaa2vLF1hp/pvcEPzC36Wb8tp5KVV/Bq7MkyfWEXnyBMTc
Jd8D/Nfof/j3CTsWT+6BO7G7HTfiCbyHJ/fEZ6j25H30DEA3uwG71vw/AbVPNmPrd69h40HXg0Gf
Mei7mzfktve/dKttdemzTPTuX8w0Q0RL7TBsK/7IpkGGrBiQ7HMVeBmJZPqchJJ6qUroIUr/9l82
ywR/yuO4xLQk+PL6odSp52y2e6E5ANIpPp51fbTcT03kubiIKzLQJAX8btIPmZLCX/DPPpkchZhK
SPZ05okliSiTGWnItCZZkJ6laFB9GBaRRINzhGY+jRv62W7u44OEuu5iLuRCD1KvdVxpb6iDgGM6
5YO4iDxSDCA8mm91j+lhTk32Yqk5+MIeTBmo8awU8gOv/X7YHOMdVam8kpiyWuAcM9ra3+82m4fd
zlEdQqlI188yj2M381wvfk8DnoWEmqq9NxdVkrNST1VkLmTohFwRYA7vZqYo+nRaQGceoJ4e5GlR
vrYScCUAKxI8QSiNtGHhoVEKtGG1pUFDwjPAN+UB+Eg3TewxOnqJIZ0Ye7nxAp0RiphRg9Uaw7JK
WSwjyHcn73hTFfSJpYmYj7vd4/3WMYu5SLnrhnmeZE8bjdFoVLf9VyfSNknnTI/Njpf3hQT0EApU
2Mh1HjIfZLCFbcraAjTlDIZtik2ca2pqdQhXH5mYTJOD/8d16wgTRoymVtK6OJdMOQOGk5lX5H4p
t8PuoZNbYOOihSbLUOnmyATz4AEHxoq+OpKmhUrcSfCDhuifpJkrhFGDMxkPDJF8pnzurI0HU0vl
r8xJbgVHL9rxPnO3JRwd1CLyFMc1f0j9ny3B06MWvtsMnB7ubRRMywf3FEWr7z8PRllZGnZweK0u
ERESGSr4NFZO1GwGK7vN/f1Ot7sIKy+NwsZI9UIzaLSbrYNGoPxGs+WoqbW6tRlq1kbEF1yfaNW7
4mdX4ZV41SUaDhztN4RXR60V4SqUvf1W3aS8kvRlwlRLN7fp7gVj0l7NU7V8nFgVWABUnTa+3AxQ
AfMegGoDQAV14d86p+leUmRD+EwDmgsWPY/rOysQG0k9mgCpHs1lshVArFTCORBrHx8dW7+6AmIO
wyZD01FTI3JqoAfhP0tPDfTUHD21WXrWhrHn09beUGtww77T+cuiJ9Ca+N62Jl6XrV2JceTLkjdr
WbshjOu0V8S4PFXjYUHQln7h/thw2N0i0E3FugTi2piNbgjiunsPELcRiIOiNGisFUSB+pCEMuYg
Pc0cvZDGKmSepGrool4roFupefMuWrt5fHAFus0OSOuTtdqNhAmtgYTaLAkEbaOajulpaNLrodsv
zLaosI1AHdpEeK3S5pWwRq5bp+14uiFYax+vCGt3MGHnpaLJPhYXFq3K/YgId0ZzeixnDGUgpBik
pkjQBzZEIINIx26l6RGiwgNjAuFg6LEYYZWACKUBpMKxSGSaI+gs04YvU9UvQhu1RZCWvlQIfSKe
qRF8TAlxFq7zI05pA+8gjdx4G8z1TXyhY59XRhCEZ7qoi4sQgQxuy63gBKovsbRRBpipLSgXXv10
yqFbG1F+bFcJ3pCSRvrc7AlEB2P9kdQiyYpch+IRwsx2zaFsiZciEImVOSGMEwMCsAFNORRJcCFf
z5knVIb4L7HTJ6hnXcdCBFhkJkLdS2WqObovEcHFV74sJqsfhOH0oV3Ma0CW2ldY0bqg7khTk2bP
OMlj3VXVqeaA+jugJmXs+l7rBJjYogLcjtvTGuaMYVtye75+rlM/VMEp7Ixa5voc4LvNuD6to70H
32cD0XxWlsbQjDxWcI/GtcfDWcW+8qTHKGP9IEIZz6LMCg5QqYOXp3ftNeP561O6tms0C3eTxpaD
L7yRjHBWRiXKlfhU5olgMRyImyMfIRsTGETPxIlbRpbcl4jz02MkKfAS+YDwE6i0KN5P/3NyvSkn
7OD+OmHfEHcjhvsTslmcXcOZIDrzC15AF/YtrJCFdphHXsmWF0ZzJoF1xbG+zxbAWg8uEcMQSOSc
sC0puEJnR6APdv16D23EWPBF7whSnlS9LzTMMWl33VEGCgbwvFmTpo1w9gXsBVQOK9GA0DQbC/zD
X+UjrC31+4oJYZCV/lBRzQvcoDf0vYkRlNtD1sxMupzNJFJCZxlgBZYWmUmsvCOsvWNNPDVhyNwH
F8aXyDexpbG0z4xD3VFLqkLJaScYFfEgH7q0ASXPY5Vl7GtcmPBC0TgDx9dKRvtr9zV6+FLH7t56
h77j0bx1R6j4GLY5zWBLjpAcKWJpiQPU2mRaw4azGv6qDhArSYMTcZKxVx2oORke4lVC6j1tQyOf
9nwmSjefydA92u+s5/msQeLaLg+3Uk8IO2YwixpCTh+mS5OGFvkiR82u4/SGfJHWqqkHBE7I2DIm
quep9M8j0rv4yw55N/fPtwl5ZyYb9nUc62WwhzD1hlAPpD+A3gYi3tCTRmQ4F96LaJxK0lRPx551
fjwkMNOrT+PdROPmZnqt5lGrPY93BHcLx2SD0xEcSbWSpOuh2Stbm3jlakMYyk3K3loGF2DYcavr
qL8hDOuuCGF2WfPLxqzOvgq3iFm5jrwI8U+WygLMQnhpQ5iFnIYHzNoAZkFPGnmRxoj9pB5GS09m
ysMcj3yglNS0X4SeTDSADIn/Xqu5AoKV+jeHYAct8mXmEQwe23RMNkiXrCq1uo1UJ+ajqRmZaV4r
Wxu13hKL5dYmy2I5jf1Znb5F5OjrN9hlY/c2tJpIXLfhb98Eqi4e/cvERfbU7jZ5OYZg0bUiSGn0
xI+vhLxus91pOnZvCPKOVoS8OxhBwh9SDFPVp7fOkOm6jgaFXbD9Zv/fR80P/6kn8cB+ubwMBzVk
WfEVwZmTPqbu3EfU650PHTFINdYssBCBvSma5/RRuUeHJuUdXrKd7GzAIwjULuRi5k7WFZsSZCZ6
yq3UiExir04POYF7eMNhTonQheJ9PSgHoqBXoAtU/Nb8L/2fCS8f2/1n+MDuTp8p3rqieBxcLte+
VG6BcH4djq8VIlreN7yL5Q6J//epzH+vytk+Z+H+zgK1D1iKv4MJiOl27Hm7c84ZMFuy5z+oXi9c
Nv9AyvKGbPlD9vNGgi6sIg1SdQ/RThMrm3VDVpsseaQ8rISUM5GeGsoL7bJclhvyUvHmDHnn8KC9
ZuhlbULXNv7PMf5tWzYnByvnRmBzMu8tLmPTtq2rYzDdZvPoyLF8Q8b88P4a85davEnNIJXUyWm2
J04mKx8Rr+zJANu1sTMTO18Zqw1vEebHY+zUDIxfQENsutRQ9jTH9fuM4EB7bAByX/IObol9jv45
mYlIx9i9nRXphRrX62+HMj63n8U2Wo79hTAO//75R3sOzmULiD9m3JFSmW2tkYnPqW6r0r/+58cf
/u/1d1O35NNludVL7snnmtlLYt69c+KFUFcTCUrekjEd2K2SWzKmX5+1OgeHS4zpBpcwUNWDMd2A
MSUVaZA9co+wIhCbHBbKPVnFdDo1m09Yba2fr7EaWWsbyrdGPLf1YGXitcnF9MkCq9hq33CWxKoL
E8QZ8SX97MsO7LUyyQHWLWHX/5ZS3Q58PWww2lBcD2rS4CHh5eQe0Lj0bFYU590rGvHkBlTXJoZq
BUQrlW9+MtDe3z+8CtFmxmTj/f61wIlVUThuXI4Xp9M7bqYLEXzQC/nznAiSIk/DfSTDgcnsARLx
Bb1maSHI9/d2vX3wagHE0WTHsXfLEHcHHf8Td5iRPdfnjeZ9+Jzra5sSnfoB+4k6FwOk8XIiDv0n
llk+Rsf4CocdwRelXsmqKVOJyezxPzYFyFaL+nAgyAn5owmVUM8upVrxwSfwR90hKzNZ65/rcm+c
wd3PZGz3CoZux/Q022d6i6ZnNNxftprUQeHNWB1U9WB1ru80Q0EaCQ8RDzpdGpfOwQq2pdSuOdvS
7B51m1fZlhW85U/Qs7YluhoFro4bHXePm13HwQ2Zj879NR9f28ABS9bmZyOqIXqFDnMhcSgLgh2w
7LbrgJj+UPnngFlewIvGoh/qwRDRjAWhHTgDQyIgdEh7aqjjR/AOOG4h8/LgKMmrAxqnfEU4Os8m
s/LxWfQMi4Z8phhnovK6AlNig4lchaUE7gfVHClZ5q9j7xARevi02SRuXpkYBys6ssFveSLNCIGX
IhH0ySEVpLZOCy5LbZg0ADH2/CtptyKBZBZgTF2BQxtxchambUNCRhUT3XXidiLQ6WFgEzFSxUyc
cgmsyFy1tGRDRIVw0BZkJkVfjWCs0ow/IKMowyUSJ/4hAuISJwvyMYelCR27k9Cw3QdtVkKFyJ3N
COlwHh/OslQx4baP/UKgZWAc6XyiHwnH9g9cwCLneFhmZqozyOdlv4+cwVxOt0YZanmMaDDzViRU
BYq5HnGLUjhvKMTRY8qeFASa2Ag7peSzA4NrORebVX90wYOu35iuW/F+hmLvbkahd7ekyLfiVBYf
Px5u83wn9HRCNfk6Z0/u5p3Lh1SljTiXrCgNqogUvmeCMfGGhQ5STAdwCGaQVnuAB48GsEcQscKu
uokCzvudx63u/np+5zVIXdslPcF+EW5N2NaqyG83AzNuAtnQGiaXk10qKIT9NjjhmICypnlPjkUP
h6cLfNujbsdJ6ZZ9279ATmfxUe9fbBEq+yE0yPCJQg8wuV2YrL7/XJwkLWnAyaL2LzTABQqVKiJd
+6nxkrAY2FyLhFwKhIfVCqHfifLNwORxt3t0cHRl6LeS0FkqUqOk5FpI95t4DQ/yZ2buv6KsU9QE
8ycsf8A15k/wS+vPUlnl5wZHb5Md8nGiAYMjTn2OZTVfdebcz+Mjx+ItY9wdnL+/1DhOY2/BNLA6
F4MVsTF4kWk+gZtkrvoaGxoHyNmruv2RTUDIC+os8r3ddlAYMd4nSV1JneBOkxjXxa+T/eQ2d9Ae
r65ze6sBsv9OBLUJmMIMB2dcB8gl5H0MyDtkgqzXnutgNhVyZlrrMh9mJn2VAqd+qpOcD71+ynPj
SxkllWERBNS1JjBZZkLZINDQ2K7Ra0SSzEjacAeVZ42JnDyC2noynk072UCFzMyl3JRqP/LskTOj
MR+adoydoAipedZRdhxJlGZyNEXB2XZ8+cF15shOxTiHdPee6ZGdMpZKg18zGrLBPmTxXLebbsmt
GcptLivMSPjBt9m+b3ONKSBpSgP9C231pkMA495zA5MnVhiXq/g0TvPmfZrDcqPaGlO/zyZxbUfo
jWtlDgD3KhCFKxYCdreu9GtaN7sfb1W/5i8wd7tofbCLmVsCubfPZYKjZc0SgNvgAeag/AHgrj95
YzVpWDeiXKwMVHaem8STA0Vm2huqMMHkrcQSvUomYql983k7+63upaMz5xwS3ymSMY23Oh5/zVRc
C7dOrdvmVjQce8KyJ8AePJQSw7Td6TNSIRFzNZJ1m82DjuPjhpBs1YPO7+QMbTYbH/fi8BIBe4g4
WxniDpVMy0WmOrmKvE5ArzWfmKPzvYqr6A6umSwf4NY4e6INFndwoA71EO6F4krJx+b1Bs5Hxwvr
NDfwOTZp2TMle+VtpS94dcme+If70VJc/hRBNTjF61qb1FgUu3dRBEJ8mvNbsltJss1U+R9MFpmP
3y497BkfbMZsPZz1vBG/nJWkgXHk0ZDy+GS9WK2yuazUrnm71DrYXzfnZwkta1usfwEigBZldYvs
UPvI0X1DdmjVw+jvoB2q2iB3dRtWvCXJNcvEUA+Ggr4sqBd8hYs2sTJlQney3DRrgOER+IYb4QrG
WB0nWLuKxTfPX7z8RiS4LtEX52osHh3uk0PxAdWluAMKyRF8ABvnPWCtnbcuSZHxernLy9Shzse2
DeD6TGap4cQvPowSl0UZd10f6R6SBXDQW0oGA9EkhJg52gJWIq4eWw8NmRPn+eBKzzRJFe45sDkT
9qiBqqRoSGsYAFQ0Tvi2SMtuD6WjRHIbSvpDZpK8KAni7BZpKlwCJYm6vGYUd/6p2H2tBO4zxSHF
yB/45z/r9sxMCGaaQKFCPrGPFwPd7aA9RbLhLkyxW4zjX3ycgu+bIs6f8XVblWucMRXNZB8LjSwv
mYYaP3A5KewiCcHPS5EBgUQqR55lFZcO6phAF8F7DMNIUgOvxmJQqIyzMbiv8G9c3m4Y4U2R0L8R
wv24HsyeKmytdHmPYb+IuRf5+GcGW3Q9q0h5WjNfmorV0or1V9Qtjx5bmbnO7DBfluDq8za7rxB2
SDySthYx5OBua5zcqzpdjK00w16Azs7t8c1EzkQmUOWrQtGf6xHBG7pfgxHZ0FsZhLv3fPDtPgy6
Gxx0t+OMd84+DLfojEfyfbLEEeeWNuOIP2z72owjDgVpTBTW/beEMdNbwSMvVWzOIz847h5eGSla
wSNfhai1XfMpYtn/Tutd4KN3mi3HyQ356M177KOX5gcgOFI9cM3zHoerZ6aXCdyrarPHMuTkAmrI
fFBpBmP7Pd7x/atIN+MrJFT09F1cu3pNHDrk4i64iAytNC6frTBfgn0dXrVeWLG9e3GgB3b/4Wyd
cy+n1VUdqpnFf9ywDsPS0/AoiGs+d1pOA0pTq51FNg8YwiOjjHgndhn+/e+cEcwixWCOaYhAqovy
Cab3WnMmNN+X7XNCX2Jyha2VIfWmxkC7VmTs5jt+WU9W3893GS+g36Dg0cDnSBnlb8f4t4qguUXj
/608Ozej7HxZlvTDCtKd8wCgJXyhB85SkufKIw3EBR7RuFxPys51GK6QGD1RuPngXKd13FrTFfgs
6tb2CTCeyySXAlcL9TGvc1BmG7h6r1632dzvOOZuyDu43ytJ72JBwIt5W4BNJ/iZFQSdiU54Pk7Y
K7QV9AKLdoKrsICtdqcMZm+iUWRpA3PFsNHTcR1NINcqLWIs/+XGLNxn1nqMzCwsuLj7IfgGBlBj
5+Ap/91PlcItV44yDiSwOQDao6AK6sLeYAUbkPCtyoImjiG2zIyGyONqPxYn2A2labJfppFiGw/X
NWmQTxbIcYQhcrBo9D4TLgEso3+vY6E/T/guG2wdSePT+yXWWzHHxcdxj5MrtmSOaVzT/1udo+2Y
44cJ+UbMMWtJg/SYrzy1AwYnw2cq48MPP22FJ3o2vz3p6Ohw4cX0y63wSkStbXzLQduwFXNaPt9f
g6qvtLrH3aPjrmPmwerOW93vkc4radpjisEwX5hpnwHAq+eK4LzbyYbTMQL0cmAvP+Pc5teIcp/Z
wPnrN69gHPoFb4go95aS12SZ2ROP7B846CmT4789XkDEt2QYXILyIjq/QbDc1eayHxCnVTbayiBP
g7xCOLfI1JkQSd28pvS+MFSfdRjdkSjZ9AVnO3N6NpKJMgjjXKmkbNYZRJtVASW1oe+ZzbnlNVUc
23ctINY9Y0zZjk64cPRBnqnia6R8KsZVu7UN+laR8Sa5TmmFDl/BQl2c8nx0soJGNpPzunnlpSoD
FV9MfwooOE+7sVrDShCCNsd52bOWRrtGUbZtdwn3kBg/IT4fplC6ksXKcp5jvRRMRaKLIhkzEQYT
253PAbkYoUlYKznwTyKOZJ6r9Jn4KSVtqDQJx2Ki36Ro+N1TSHz1MT8nZh2XALoi1vn1DrSeG3bs
UG11jKHF6oDC72uPnnc7c6Pm3c7dHy3vdmZGybudcnTszowK8Eaj4d3OzY6C3aXa7/RkDVXfXUvF
b8n3zc/bW/R9sWA8POPdhws83w0uReE+3QfPdwOeL+lIY4pE3hQcPSisFxM4nmGLfODFSbSKI+yU
bt4RPmgfrbkytQ6Na/vFL1a2E4uc5MN9x+gNOcn3eeHKpc3iqJYLxkqZnYsTxPqRuiAQyhjjbBlO
pHC38NlsBBcglIK6NBu6jcJuCaQ+k4s1PVmblICkkdsdNmhqkdfzdoglETRry3GyDWdPuIyO0CB3
Y6881GcsFEk55HUKfENk6767D50TU/aQGeG+xnc5ojPS95F5gQ/QRwK967wLPJsw89Jm7zAEIyel
LNAr8hxJNGyXlBMEDoB4xO3MJ8H5Q1z9zkX//fOPj8vVHwSAkL3DK0PMgeR7YLVfhDIV7wuFLZYn
LPA+ib9XsCwTEFPgsAklvj8lf4w6JiFqnKOBx2S4kFJt25lJK0PIy6YjkeWdPakRRj/WRcSfIRDG
OjA5EQgdsUcc+2HBu0ltO0y6TeP2Ebiqso0gFEqVOd/LaM/q4uswM3ypCZbESN9ynMvkn49Lb8lF
1qiAj12rOZwwJ3uckuRaieA8cYqK7TXqDl+lOXrZNzWTBrg3Es2xprmFOx54Ltdm0pcum0VSk/0U
LRrb2/RRIufOyFzkt/cUHBbWaFIdm4MET+bZzDjh847cR7wkOGavEv1OEB3qXtt2ZSrhillFclS4
vvvum7eNNz+dvuWRqnAFjO2TwFQ7pDzLCLImsnCBJvlYYyRajd1JSiFpagHHrgFf0F4GM8mrdzqa
lTty+dYZfc2F1BsDot3lAIROYx34ItBm9/ZRZncL6LL7xaPKpfnQEgjZvWHo2L05yLiladjZxTbv
hAqLGLOaZSfBb3AF4iEhYEPzMFKSBg9Yj02K58bzKjMup1/z2+OPj7trXv20nJq151ZXWMxFk6hm
09F+Q5OoVVcaaIyEMLYTROZL8zY+nyo1reKi3BJWHY34DLktYZWfFuN+/3g7UHW3Fku59VsDqur7
z0UqUpEyOYjcIi+/8DL81DFcZ/ZKVwEtp2hzoHXc3W9dChOV+YhXjsVGu9k6aLTaIKmWmxqRVMsv
aiCpViGpziNtvkPKwWbD7aKNbSfkYaHccihzuUvfkVv49hds+BiJk2ljC1DtsHnomLtlVLvJ0NBd
gbKLpmpt82rtr1PClvT7V0VsPi7Bsw2GwHHy0d3BM/f+1hBtbdeLFaUR4JzFyZV9Hh/MSACHmUiZ
80jDm88H+jS6TXRvLghO/zlaMxtkPSqvAjsnl+X49gLHMk4amuwk5ImZW93CHItPcqusmV2dr0mw
fnDoGL8h5LvXQXHMzc8x09cZ8arTkCa5vQttCndYRYYkWZr5JsogUR5TbhJiGCIqwdseEWhITTxY
FB18O1Rj9GdsCuwUpeLV1IZaoiu5CY8yKhQgiY9mzVlNjRW9V48nAQui0V18wxrR45iIrbia8pCd
6yQZV1IlFpOGTP5+wYu9VFVP9oh/mWokI5VL1qZPk3mdIC4BfiUsbporHNlRWaOvtO5rrAq7ZOIK
FeJfJE9ejC6rnu5J4ACcurpqG3QYmTSgPuHYF85pRzzFbZjEiSEcmeEqMCycHMt4Gz4t5TdpAWi1
TDKRQSaljEgVygwDrsDXtb7esxXxQCwZJveIvR3mMzA0zar0u3uJ67b73LH885l4beriV2xWxVL2
wiTeU7NXRuGIBL4tgFofcuRluuO1bI2jb9xl2IbCyrCcUVJq1ASEmSn4+aHdzQ0nF62dGztIdKAx
8W7nemOFqmGxvNuZRIU3NRCo6pkBgFyMO6j4Jdu3ouVo/PoqPWWhqr+34/C22+/Ptujw/vDTC/WD
TLFGvh1/t3W09+DwbsLhhaKUM3jqGfogHOMPO4A81vEVnNxS3+Y3Hh22W+s6uStTtrZj6ybuZeUV
3GDQBBxf6cMilXvf8XVzPiy+kWTKMMDcu57qW/3F7z///H8bvnDk9sUAAA==
headers:
CF-RAY: [24e5676e721111c5-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['8479']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:43:59 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d116e150f13bb5691702076a4b802a12a1449042239; expires=Thu,
01-Dec-16 07:43:59 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=bYdrnTcvwMFltcWm6CYr5oYjtOCh9D1teKC1G5fxD4ljTChBimcpnL8O5wbCSVp2gECe40qeDYU%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,31 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/invalidsubreddit7/top.json?limit=1024
response:
body: {string: "<html>\n <head>\n <title>302 Found</title>\n </head>\n <body>\n\
\ <h1>302 Found</h1>\n The resource was found at <a href=\"https://api.reddit.com/subreddits/search.json?q=invalidsubreddit7\"\
>https://api.reddit.com/subreddits/search.json?q=invalidsubreddit7</a>;\n\
you should be redirected automatically.\n\n\n </body>\n</html>"}
headers:
CF-RAY: [24e5677aa46a1ea7-SJC]
Connection: [keep-alive]
Content-Length: ['313']
Content-Type: [text/html; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:02 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=df3faf3331eff535be76b2182b9629ec01449042241; expires=Thu,
01-Dec-16 07:44:01 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Content-Type-Options: [nosniff]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
location: ['https://api.reddit.com/subreddits/search.json?q=invalidsubreddit7']
status: {code: 302, message: Found}
version: 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMuwrCQBBFf2XYWiGiBrH0UaQQG7UNm92Ljhp3mRmDQfx3iVYXDufct/MhQLW2
dMPDLcnNZuWimEzm4zzfbKuwCNd2vdtvduWpOp5PdriPV5UbkfsFtfUZQ9XAC2TgeGUWaM3D27Qs
ihE5DemvIbLRhdWS9MQRD2Prqe312QhiZFPKwp03tFD1ZygJfCRBTmKkvgPps2nZhtEg3IC6ZHCf
L2mPdxLKAAAA
headers:
CF-RAY: [24e5680fc4e61ea7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:25 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dfbfe38c66c15c17ee9c54aeac98936b11449042265; expires=Thu,
01-Dec-16 07:44:25 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e5681c46d81e7d-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:27 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dcab57cae270bb2279df0c2f79274ab6a1449042267; expires=Thu,
01-Dec-16 07:44:27 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['599.0']
x-ratelimit-reset: ['333']
x-ratelimit-used: ['1']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Cookie: [__cfduid=dcab57cae270bb2279df0c2f79274ab6a1449042267]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/user/RedditTerminalViewer/submitted.json?sort=new&t=all
response:
body:
string: !!binary |
H4sIAAAAAAAAA0XJwQ2AIAwF0FWaf2aCzuAGxgPaIo0ICeCJsLsXE48vb+CyLGDCYq1bPuEI4rsH
08BdJPoWwZSflBzhiJakagbTujmCD13r37uGUvXznC840NRuXQAAAA==
headers:
CF-RAY: [24e56828075d1e7d-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:29 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['598.0']
x-ratelimit-reset: ['331']
x-ratelimit-used: ['2']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=lN0fUBJ0AoQtSibyqvlNLk6Y%2FE9HOSPds3XzLc0iY5w0g%2Fsj2RbviNMRCWuWOPbzHX2OfFnYnZUf7aVGbZweVdoFLCSciF2O']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,567 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/python+linux/.json?limit=1024
response:
body:
string: !!binary |
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
headers:
CF-RAY: [24e567f7c9d60669-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['28537']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:22 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d0550ee5a87863dc1a10e0f8979e182071449042261; expires=Thu,
01-Dec-16 07:44:21 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=H0HWQSiFoJkh3tOeOXSK7UwVX04aHyUzHsZP1ZjVw2j6e5F6XKWM3QO%2FCNEL%2B1n0XhTzPU23YLA%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=d0550ee5a87863dc1a10e0f8979e182071449042261]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/a+b/.json?limit=1024
response:
body: {string: '{"error": 404}'}
headers:
CF-RAY: [24e568024a690669-SJC]
Connection: [keep-alive]
Content-Length: ['14']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:23 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=ULKY%2F6vNuEtLn981hC1I4%2BUyQS3mH%2BNOiUBWiKMscP3Skg968mr%2Bbo2v0FeOuh75xsBg1Z0rmvY%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 404, message: Not Found}
version: 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMTQuCUBBF/8rw1gVKJdGuoEWbIAj62MjTd6mh7MnMKGr038NaXTicc9/OlyVU
c4sPvNyK3HyeLZM0XUxPcX3Y8fl63x9n2+bUPYdnurbNsMkubkLuF+TW1xirAl4gI0dXs0BzHt9m
WZJMyGkZ/xoCG91ZLUpPHPAytp6qXptCEAKbUi3cekMFVX+DksAHEtRRjNS3IG2Kim0cLYULUBsN
7vMF6g45KMoAAAA=
headers:
CF-RAY: [24e5687b6591012b-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:42 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dec07de54f5bcc3b4f496dc4a2de773461449042282; expires=Thu,
01-Dec-16 07:44:42 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e56881fe1f2888-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:44 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d69b01e4f4e78cc083016d3974b83143f1449042283; expires=Thu,
01-Dec-16 07:44:43 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['594.0']
x-ratelimit-reset: ['316']
x-ratelimit-used: ['6']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,78 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMwWrDMBAFf2XROQEbJ6bk2mI3EHBzbC9Gsl6TpTgSuxtTUfrvxenpwTDzfpyf
JqiOlr5wcwdyu137VNX1ftsPL+m5Ow357fz+2eSu7oePvn3153B0G3KPYLSSsVYBXiArx3dmgY68
vjVtVW3I6ZT+NUQ2urJakkIccTO2QnPRexDEyKaUhRdvmKHqL1AS+EiCnMRI/QLSe5jZ1tFJOICW
ZHC/f8fwa1rKAAAA
headers:
CF-RAY: [24e5686084e51e7d-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:38 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d97dd5b69ae5538390d1e4ad773c2b04e1449042278; expires=Thu,
01-Dec-16 07:44:38 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e5686b66a111a7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:40 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dae4a6a80cbf9100c339be2eeca35c7bc1449042280; expires=Thu,
01-Dec-16 07:44:40 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['595.0']
x-ratelimit-reset: ['320']
x-ratelimit-used: ['5']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,115 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWM0UrDQBBFf2XY5wopJrH0sb5Iq2klgo9hs3ttB0k2zExCgvjvJfp04XDO/XE+
BKg2lr7Ruz25PC932XZbPFT28nqy+uNp/joX/SU9V+Hw9tnPx/rdbcj9BY0tA9aqhRfIyjEPLNCG
17fHMss25DSkfw2RjW6slmQhjuiNbaFu0bEVxMimNAhP3tBB1V+hJPCRBEMSI/UTSMe2Y1tHg3AL
mpLB/d4BUZlC/MoAAAA=
headers:
CF-RAY: [24e568ab052f11c5-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:50 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d61ebc6182efabecafffcdd6f17eedc371449042290; expires=Thu,
01-Dec-16 07:44:50 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e568b64b372888-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:52 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dbace6132db512e760a8db06293b0b3eb1449042292; expires=Thu,
01-Dec-16 07:44:52 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['593.0']
x-ratelimit-reset: ['308']
x-ratelimit-used: ['7']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Cookie: [__cfduid=dbace6132db512e760a8db06293b0b3eb1449042292]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/message/unread/.json?limit=1
response:
body:
string: !!binary |
H4sIAAAAAAAAA0XJwQ2AIAwF0FWaf2aCzuAGxgPaIo0ICeCJsLsXE48vb+CyLGDCYq1bPuEI4rsH
08BdJPoWwZSflBzhiJakagbTujmCD13r37uGUvXznC840NRuXQAAAA==
headers:
CF-RAY: [24e568c12b752888-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:54 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['592.0']
x-ratelimit-reset: ['307']
x-ratelimit-used: ['8']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=GiNcIZQuFEYlmAsD9NYmt4RztqwZuomXoyJ5rRJzDM8C62C1Y%2BLxZ8x02nBUmSzU1VDbrdFq0NTwBGV9usQLv1EScW%2By8pRf']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,248 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
Cookie: [__cfduid=d963647719488322ce24aaf79235c3d6e1449042300]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMy2oCQRBFf6XotcIMURG3bmcjrrIa+nHRSjLTTVXZsRX/PUxcXTicc5/OxwjV
0fI3Zncgt9ns9l3fb9fdfKxx/zvko38Mp+ErlL79nOr5+nlxK3L/wWitYKkCvEAWjnthgY68vH3s
um5FTmN+a0hsdGW1LI04YTa2RlPTWxCkxKZUhKs3TFD1FygJfCJByWKkvoL0Fia2ZTQKB1DNBvf6
A0kzhmXKAAAA
headers:
CF-RAY: [24e569196e1227fe-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:08 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e5692852e2288e-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:10 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=dfdd374e9eea56b2249bf4ce8d4e817611449042310; expires=Thu,
01-Dec-16 07:45:10 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['590.0']
x-ratelimit-reset: ['290']
x-ratelimit-used: ['10']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Cookie: [__cfduid=dfdd374e9eea56b2249bf4ce8d4e817611449042310]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit/.json
response:
body:
string: !!binary |
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
headers:
CF-RAY: [24e56930d335288e-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7632']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:12 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['589.0']
x-ratelimit-reset: ['289']
x-ratelimit-used: ['11']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=Bzh2jK%2F5JKtFD80jsp5q7gytwuV0LY2xuBeEwR8znz4g%2FJqJKBsaRMA4%2BnJ49H71F%2B4ClKNNPbc%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,249 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
Cookie: [__cfduid=d963647719488322ce24aaf79235c3d6e1449042300]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMwYrCQBAFf6WZswvJbgxhby7KXoS9iNcwmXmsjcQZujshg/jvEj09KKre3fkQ
oNpbuuLmvsk1TdtVdb39+Mn75fjXjeel7KZf5uYzWHvqjqk+uA25V9BbyVirAV4gK8eSWaA9r29f
bVVtyGlIbw2RjS6slqQQR9yMrdBYdBoEMbIpZeHZG0ao+n8oCXwkQU5ipH4G6TSMbOtoEB5AczK4
xxPWvTOCygAAAA==
headers:
CF-RAY: [24e5693f1554288e-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:14 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e5694ba1401ecb-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:16 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d106c081e048a4c399da689dddfb3797f1449042316; expires=Thu,
01-Dec-16 07:45:16 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['588.0']
x-ratelimit-reset: ['284']
x-ratelimit-used: ['12']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Cookie: [__cfduid=d106c081e048a4c399da689dddfb3797f1449042316]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit/.json
response:
body:
string: !!binary |
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
AA==
headers:
CF-RAY: [24e5694e61591ecb-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7639']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:16 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['587.0']
x-ratelimit-reset: ['284']
x-ratelimit-used: ['13']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=2yjiYfnPd2byM5N5coy88KnkvDIRy87gGDPHOWbe%2FP5ePaWKOeJn3NNy5lf2rFhGOL1qr4VMvE4%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,249 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
Cookie: [__cfduid=d963647719488322ce24aaf79235c3d6e1449042300]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMsQrCQBAFf2W5WiFqDGqb0lYQq3DJPXSReMfuJvEU/12i1YNh5r2d7zqoNhbv
eLgDubKsdsVqtV0iDedjf/GvNJzzaR/qqeZ1XW72084tyP2CxnLCXLXwApk5nokF2vD8tqmKYkFO
u/jXENjoxmpRMnHAw9gy9VmHVhACm1ISHr2hh6q/QkngAwlSFCP1I0iHtmebRzvhFjRGg/t8AYx9
Bq7KAAAA
headers:
CF-RAY: [24e569649f492816-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:20 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e56971a0f811a7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:22 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d667511ab17e019fb769e680b9cab23071449042322; expires=Thu,
01-Dec-16 07:45:22 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['586.0']
x-ratelimit-reset: ['278']
x-ratelimit-used: ['14']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Cookie: [__cfduid=d667511ab17e019fb769e680b9cab23071449042322]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit/.json
response:
body:
string: !!binary |
H4sIAJKhXlYC/+1djZPbtrH/V+Brp2dPdZJIfTuT8TixU1/G+RjHrV+endGDSEiiRRI0P3RWOv3f
3+4CoHSUdHeiSOXqXNuZ+igCBBb72y8sFu//fbbwQvfsKTt77SWpF87OGuzM5SmHR/8+C6Q758kc
/h1mvg+/OHPPd2MRwpP366Zp51orVwbcw1fOZl46zyZNRwb4woSHoXDHk9W6v0C4Hh+LYCKwo3//
Bx4l2SQWruul2MHPq3QuQ2ycCH+ais/peJ4G/roD8xhfxtd8byGSjZ+z2UwkKXw1kTG+pJ9niYjH
sYjgIb79/jfqysliMaYhrd/0vXAxnvrci8f6O/oHj6Zufw5kn6Y/jWUw1hTRr8yAWDSxNvzBYyDe
kv5M40wgMX3PWdCDKfcTfKJGBAPjiQw3psEzIEOM33O8ped7v/PUk+E4mvPfxZi+Xhh1yAOBr6ed
8XqEiSNjfGr3+thnFMVyWVgPeBCPreHGkOae69J6mwewosEk5B4uApE8X7CxIknaG9uf5u0V/gaj
Sq/NcIOcTpKMHZ8nW/Pc/7srr4gsSE/gzZtWsMBUXFF+TehALrmvKb3+AEDAWXjXXsV1Xb/gJWNk
ucLvaub6lUjEAcd5IjFacUvxcAtAEIgwTVpqQVp8HNEPwFZx4IUwmKUnroD8U6DAJJZXCaBxrCjb
ItaWBW6BpRFjs6jmoQNTUiS3unbPHnWtvt1EemUxrdg8TaPkaau1hmYr8Jw5F/6Fz3/ncStOl/i1
a0txnfM/ZTzmIQiLze+mXuoTyz1nal7MzIupeTGYFzPzYhri6wGPs9Qxg+73OmbQES6uYtgsWspU
jGPkfXjYbo7waZgFY0NaeNrtwrOllxTYDt9aM4vhJiXxMi+Z08v4+D//QUbiIFMQbvrFiZgqIqs3
GqwaqWlda3UTjHZIzn0cTADTPWyCvyAYgRi+Rw8e5vIwl4e53H0upPN22g8FHaM6dSJ7tOpk2OgW
c+B25T/hLqi4VMYJdhdxIFE+e2sMXxpmgwn+lGt6bL5LzzsyTPFpnHigqlL8BQc1kS7+8+xXkbB0
7iUsmcvMd9mVjBfsCjQG477PQHUw6PMjDCVhj9/LdA7SPZJR5vOY/evbXxgMjCVZhNMRLuMJuxK+
/9tjo3oiL2pGq4g3PdkSYcsH8Z+krVhMBUzIEfj72AuTFL7VRGvvL0snudD9PXnS/BB+CC+nbCUz
+Cp3FnwmmOvFDIcrA3EFoxFsIkJQK3OYhWBIWBZLmTawEQu82Txlc1gtlkr2/mMWRPBaLLMZTI9N
xRULgHpsLmWUHDFo8ZkHkS+SJ2ySpQxIhuNLPaBfJJPEm/iiiUu1ZSEdYgPhehmD+OxvfvqV6y0Z
vf/1h7PA/XD2t1n6FT6P8B+HrCo24mwO84OeStNgY+HUWO7IK/j1FscGT5r0bxo/Lnw+meo4oJqp
muVW87yRqdaz288am7Omf8PK4l+E7j0eEoJ+vGWv564ASQgji66biZ1+p9Ppk8V1g923bal1Ot2e
brfLmGrstNJ32mLa0Lub+XWY+DVC8Wjx+zP3waSV38s50Xtb/lrxyibVdqz8daWBKmEULeciRJ+d
WHjcZUh1Ma1ZwSLTWr1+twzT2gPd7t4ybc5JRzPtbTaDtehbFJ3JeRY9qDJM+09wCX1gEY6iNYpE
mDQYD1cyFMzhoRZwWmai0Ft6nIEYfYpincF//op/5S8AY/39Lr5qc6bcyBNCoexEd2iyWJieHenS
v8tTQWPOdKS+pj5QKRxz3izAsWP1hu0ScGzbA92uGjh268CjQcnReHT5UgJnLeUeOM77A3cTjlZZ
HfJKrB6xGZLbCOgGu0Ke5RMJVkfAFxiHQfG95jcOpgeYUCwSEmwZ4uY0XuFL2MQLwZRJQh49Q/59
/0b4YsnDlKUZiBcYgzKW0XgSQHrCF3ErGDVp0pp6cZJepF4gLtDmvADryiPT6ckHYr0TArhO0uwA
ufn2NSPzYDKpOWwRnb5HxmRN2jdn/QLc23a7bR0Od3vQsXW7SuBu2dcDcvckSvCnjnhYcjjjlUjL
fdsdOwSn7MpoU3CWlZtv5zxcgEa/PAf960u5AHCDl0imbyrBi4vl1ak993xMfwMn86u/dEZf3TC2
uuSAWdSCHLC6A1sF6w+TA8PeSLerRA7UYoUbnjqaj9+7ILeBAL/tYd6i1i/LvNe+c0IGzb9bG/Pp
ldhiPrvXK8V8Xd3u/jKf4Ymjme9uQnTnTrGFHZXgw1ceRgcaFKo9XwoWgY2Uypmg8B9YSgE6N3qf
UJtgyqGacgfGmmK4bb1Z+Ia4ZiOelm8tXoGmBJHHLlMKSyTaqsPoBPIAuGUu+0RCk7MJT4TLciZM
oBWYeq5Q4nMiHA7LA8+uwLzCF2UAw8XP08AumUNBU56m3Jlj3BCGEWSfWSKSBAhLn1oIgQYjxRwn
3FnMYMDw+Aq0u6ABYnfwrgqDUu80jCm9lniBh2HRdRRWNGdN9hzUcPidEO6TBsULoe2KuZ4bnqcm
cromlaERTDPw1MDM7ijauOA3YrDx8oefaIwBmJzMC7ALtCmBIdJHSAGaqS9VfBRWDd1bNoUh4KQU
PTCOOstWyUYw9dRaUTPZHot7rSvvB/99mTyYE/m+8WJdishI5WKeRW/QGZRQRP3+SLerRhFZD97Q
w1we5vIwl3szl4OjB/biszXHRicyfNUW0+/JAH/Kw67YUQnD9zU45QlDYpBSA7WXgdblCYtFIn3o
DFVzp9ltWrhn4IKCm3NQ6BFSC/6fTXzw9EHJTb3PZEXAq4w+BlNUyk6EQBSWRdAjLKgKVWKHnSZT
YQJqR3vjc+FHj3IbfCbTVGCcEnejY/EpE0mqXl5K5DEaz1pBU/wj4DARHup54LueMmow9gn6mPbQ
11YKmDeeg2NLpAybJ4/r3j/i38U2PXRdNmNAR61PTSZSjt+Crz60h6MSAeNOr9vW7SoxkawafPVc
fBwtst6IQLh8T36B3ZYBCfVjpRQyPjCGWlfDwO+MRX4lvNhtgCsQOggkjvkpyF4YcNTwAi7bhau2
Ss4y0oYznizYxQUl+aCRHgHAQnINwHVxXeB4nmbQCbgI8GnocJvj8U9ka518MJO6+USwjxl4DBwG
AQ5UfOp8iPpIeFeZcVLq1iUrDHC2UjuG7VEJWdHptHW7+ysrDIiPlhV3N2+saOlR2tex8eW3yCXw
P9CV2cxEGXQcJQG/3eUxOOveJObxqsk+hHqnE15Omuq1poxnLQKDbbWHQ2TxF1Jn5QFfelPQysjX
2PEUxmQUMAwwmIiYyWl+vkC7/ufA/hkGHE4tAg4lBjYs7gHfQBn62K2vERi39n835MaR1M2lTk7l
ukSBwUVBFHSHg1GZtBJrNNLtKhEFtYT4DSyPFgU3mg1WFDmUbmLQ38G2JdD/D6RygzQXBTvR3gwE
CzEC+o2KCuaGKmklQAa8HIgEo55Z1GTvX4lYnCNkElgyESZzmTbzvBGv6QWzLKZsiKno/a/1/awZ
hbMnJ8b18dPchfQ9k1PfRrLkSCuQZzfCK0RezoYF5NkdyyqRFGyPuh3drhLkdWpBnobE0cg7QAnL
ZcfZhOHeU5i3wPCVWDVQPht3k3zdcCoxXA52H/AgPvnX5Q/AlqEL9E/YV0/IMCeLkzKZwMaLMUTj
wOIp20/EMfQ1WbE4U1bkhzOjy0SaYTa8SYX6cKZczDl4rYFckslokuxpT8L1ppQnn4K96xBlaIcE
cyV5rD1pZRrjQWHsGcaF21lah8bpsgUzg5WJCYZ6swuD/9DKz3CK+lXksAt9BgDmMJuhI+7lRxzA
jgVnHl4H8DbAQIZ1BCNcIFjZAp6hRjQ4hzGeBzp7E16MlSkN3wgzDmNNSDnq0wYBD+H/UAKcfNOr
zOLvMQ0O4weST58yCY1388X6hVr4Y2Or6QY+2Xirfn7JP1aCb2oT5lqyFYW5bZfxqOxRp1ulR9W/
l/tTB8eorWg061eiP97OxTff3FFhlA33/CqiBogHxAPyZQL8ouDdZL8IaItign5ZekEuNprszctf
zKYuglIEgF55anlX6dhrw5zmhgLmrNHQ7pTBnNXW7SrBXB1RjJw3jwbAXtdlV0JSWZPpEh1fUGTE
KVPMhKAYvcntRhWE2hOzKFmwAnGdznOmM7owP6jyNuaOoOyDx5S0EAsHdZmDOSXAxumTp+o9xr7D
L304a2VJ3ALebIHC+gDTAfkk2KiBOgm5EFYz8/U5DdWOgWLk7hh6jVfjSHph+vgc2n79tdVsc+u8
wc5hvon0sfxD7EVpgo/ghfMnj5/s+rbvTVpKaXea3RYWR8h1YCtaYJWJBMgBuqo1HnshiN8xKN18
pD1MA4OxFse0Hm0MlkAcIr3GyLGxN8mQpI/xjyfNYrvHmJICqEYM1TBcu9+xbxmvGbCIaHB1EA2c
p/UotiiFNUSaOvRb09eH9HX9jY2pK25jXzNoR9k24/FjGo76YYyr0mBY1cSH1fv6/fmYHo3H5yAP
fLEU/tdtM+JL6uAloYP9KE3f+L7LztF4Oye/w6DPmHPYO0HR8SUodWUagl2VCBEQBtUZQ5iJ2lAo
xtoxf+k5yBr22guzz/TOyxeXb5+y78Bkxe2uPEHoqUrN0rYdCMDYm3oOmnEYFFMG8zVjGW1q1wNI
IwMntK1H53PnrY+tRcvHMLovpmkLNVori1oxphydWilWIs52OAXbx9HuJuuIcdfm/6a8Wz+9Lvfw
8Vr24V+5/Cvi9vE6BpnLwPxRQ3WF/yzIxF2vmAHhv7Ws3DX2UuDbmunNcvNImVnfsG+Tn7tkZ42j
2Zaje2VoraPYJU8PlaU57xmZmj+4LltvlKt5G5Sm+R/aoN19+nPT5z9CFu+QGNTlLeJ5T7M/VmLX
5QoYu7joCgwGnVKnZIYj3a4SV+BLcb/nC5hOowLv4/sJj+NVt9+7m//Rw8Yl/A+zVehI6TebTZV8
dIURoynuy3GGad8qfefEpsQBI6sLMmYxr0PGHvV7pbzn/mBQpffc+0IgE9kLUQlkOv/oP++963SG
L++GmbK7jT96jgCOZJ4ruE8siKzIVfSVPf2ZzP63UrIJd9kVHck+T7GCTqqKGnknN8tvHfEeTXh9
ErlKL0ymLvgZxihqrOHQLnO0btRu63aVwK/zhcAvnHnVBIxfe2nqi5eRDLEAZ+MuCCyb7fP8Sh2E
DpiTxfgRf7V5ggiTw5D27NvXmFVClbvUDrnLg5Bc0kRnvMF8fGgcAkBobwhfBf/Rc7F6QhNPG0Vk
a0KbR+zpqTf89TzXhvWp51sXtA3TFaFt9bolMnHtoT3Q7SqB9v2s3HA4tKPk47QSaDu+d/ExCxde
zbj+h5Sq+h/urOAWMDItMjmqrHW2uE6k92hnHPj375bKSvcCthCrtQNmoHAYaonO5TFbYg45vu8w
l7oQaXhla3e2P9i/O3v2rQwCFCOvMRaBwyE22IHQUWeo+3lA6AZCPwm3mjoOv8y9WM69Wc0AvaT6
QqFMWYqKAwMfc3nFAszjvmR0GBafUeRDpWwTh/+wAh0TpTJCh45a476oiIG/BSZzk2+HUVI8YQvq
KYF5z2a+UCZmJGLoMMCcBVRQE18EdE5XhEYJksX9IwxEjc/xJZq3PtZCw39p7ZjyScJ0TEgllIPu
1OkXGweScTQwEMweoWw6/BtjbU32I4xchjA+XXMQDGg35gFPPQw7w2OBnA8fDEDDCuESHbifXsCX
1fFkWH+KYU1jAT1kEdrhLnvz/AcWABPGK+U6YBIN0lHn0xT+eXoz5L6u+h6v5YtghD1zu5036lIQ
RlQVFUSvPxruVRD7TbZRf6jbfcEK4U99XNaK+4uP2Oh45ZYKHvwM9uf4hVh6Pva5Iykp7vh2zs1n
5ZOS3nHAJaEW/KAsmsVYBgM3K9U2wlO68+KE0vfW8dSFeLN+RcQPR90S2deddtvS7SpBfC3JQ4aH
6mfZKq2yF1IpIk/XDuegk2dq+x3ciJlI1ek65sQgbqhG5ktUg1SN3KMsNPUTM/UfeZIK8DzoSMDE
/ua7187PtHd0Qq4/bE57tOWeaeLvxVMQO+d87ajTzjfow7WegTAsuYXCfpnC6PZoNKy4MPo91LuH
66pk5tFO29HAT1ahOyE9Xx/ecSP5PAGrGFAAZrPnLB4xCjt8lJMGczNXZyelGBxxRJxyD41VPPiP
+eJox+r7DlTN2CzFkyKqNsH/OVmciOT/zPlAZYxTdg5utedFqTA/P2T6coEDIyxHCwckQB40uZkQ
eyTD5br94TTCXkw+gyIXfcU8Oox0dckNw9EFudHpWO0ScqPT7nZ0uwe5sSE30kH/cyVy4wexmPPA
q1dwXKqSZymsLajOCNowXdcjP7IDWi4B/9rFotHT7NTAPnyAdcHHLGwRPj0AQhn49G3d7guGz5/a
3bWttt2uRBQccHIzE/NKDuI8nyTSz1Lhrx6xSzw87MP43BVeC7SgM2rgev785vk73NPA/Qt1UH/O
YxehCQPbyIzDOo8qCIgHAME7m3IHk/gEV3UgE++PMBn+gBnWJJpyRiuIpu7ILhOJ69jtbpWRuFr8
csPnR2MrAgSILGkP21TdpT5V+1zXvpGRCDFwA6sg1JkwDECrOjjrs/qhOh2L0ugZozJFoYCuKdIN
3EmGZKhMTRU/vtywWYkvMcWISmmk8Yp4U2JoXWVviyZ7+uLUeDti/rlpXoYOG1lKN9KjJnTmrFo0
HAaDGzZS96OzPRw9bJxuC4SVq+72PlogqPnK2LLaRIv6JIJJaKULXS4pontqJbhrCHXhwKxQUUv1
TBbeYTiw+naV2Xv3Ewd/agPaCi1B7HU0pg8woIOPnBLRDLgH2FEJcL/ic84SGcdgXn4nhE8bu6R0
5sJZ0FVMqODUkR48CoJBaU4lenzcTJ5qo5Fsz5neU1ZF1RMq4ABmpzpiQolDdOU4vEhnRzaq54EJ
SmUtjGZtMnUdKXMlGgjKsMW/VdeTzMMP5ElJdOQmcWKeOvMGS1UZUSyokb9BBepBIev0JY5KFkub
Y30KPPaHUftHJ5Zq/wW03zBKTrQGdYl1A9KCWLfabauEeWMPLVu3q0SsD+pwPoyMOFouvQE7dRVI
mNH/XG7LoyptjV9/+if75e1Pr1+yH35lly9ePqdqy99j5cwFrC/QWeUHX5qEYKw3TJFqPCKgLOvz
gJJ2OJthRF2EdGUCDGUWc2DwGPmMYIPMjscA2PMrgUyKBX/mYPZvyB0yxDkm+PBwRjF1QWytKvlh
WiRC8xE7seDYptImajZ2Cw4j3Ea6diUELHo0dyBkXeg3UCigvz0sdUuzPRhVfUvzg1FX61wONups
K/pUzX3BBxh1n6zl1aYULRsVxTS/529/IFRPNw0LukkGda+6R2ZtXZAhQLXPk2w2Ewmd/D2xVKto
1DWJkJwhCiKk1+7aZaqp2uAYVlhNtZbopeHHozFw8aPnXWwz/S7LoSzP/4rJvZhwMwXLl3bhltKj
KnVvaMXpjJED4gDjdmR8mmJ9ZGej7cmCFdUa2NBnlH0Tq4r/aKqT1Z1KeFeqCwnMxVJYjsB0ixm8
wOP4BAN/p66ifI8pURM2c0YtYNPulorZ2KNepTGbhyukHubyMJeHudyfuRxsDlvJqqL7WPbmC6nk
r6G8dmtU2TiCdx6A9xqGHKvuUI1Z0kXqdp9d+TngCM8yuuEH7yqiv6msDeNXfHXq7Q5vwxOvcBZ1
aV/DG8Wdw263X+JQdKfd6+t2lWjfWu4ZMHx6NBz4R+CCWNWL2IUHe0By5ljreDM3hA6xqZNsWBgf
t6i5OjhGNZrXlaIkg664TkBX58jUNV/a5cL0dCwMhZEyiuLkKW50bCxcqbCvANo1Tx0iq3fCG5Gt
WydeG+o0CxZRZ3cHJe5N7bQ7fd2uEtTV4o8aNByNuitwWBbAbXM8pbkHevEqHFahil7o0yDEQ8g+
mLFFOVpAZxmvTCG2yIv0rgjtpHA6M3Jq3/G4wdbG6nrdi6xujcpEb4HVrYqjt9WzuuG+o1n9FgUT
x7/T6djjFYwX8BmeFvDwiEcqsOAiXdOUhyMoskA1n9+kS3aR4dHhGBeaXUS5BMWf1aVv5u7ulbqQ
ygujTJ2dkurA8+bJZswN+REPXfvrW8LhEbIs5UheY2GROn+AOlqTZ+MAyH4ybUJpY0PnBsrtabFF
zPzz1RO1LvwbMGzhv98pE3pt20PdrhL816LqDC6Pxv/N/tanLL4G/7JK7p3kKjCfW0KeKp2gVhbZ
Cz4dr56poCAYjHyG0X190gjrEAjHo4vTTozMA0e+YfntnUFtMNA8sQWDdqdECgPAwNbtKoFBLWrQ
8OfRMNivBqvchHir8xS5Cps3MWzuUdXBxNOXAuGQ4OvmXqANvqMSIWhcic/CyVK6jYdhBXLBXQq2
ryh5H/qjawNB9uMdiDo7IpHmwJ+CFwYMqFC57hzHjA/UtTwiPrkGPDlp9ujDbWptQPp2qtWFbcPo
xR2MQX9QQsXZo+FQt6sE2w87GHXP5eCIrG0t+tV4CAckKMTTNp3NPVZOrqMzdCCcsxmyMKUiNQpX
sIOdOQMVm01OnZ25OcYNk/1uY61JTOSLvpWEYA26h4uJjm11dLuqxET1JoDhuaP5fBGvIlAuY8xF
2+bvas2AjdsE1zrPHKSnnMbLjXN5iqNQA7oqo5ahdELF5soUvjX1dAWRx74EJ7KRK6gGGJ1Tnvkp
Hj9WTNVA5SbDJ8ZBm2Z4ZXmTvXvx69vr0STiAKvXG+0IF1aKpNvIsUdT/yEUqgm4ORcX9ftoMCq1
R9Ie6XZVAfdBvz/M5Y5zOdxWmfhBNYH7N3IFM3gFNrwPKqZRFOKkI/vhiOpWHSvF36mzm3RagTNn
zuPAXBT7iNEdXIVEcSy/ksBazRp0SgN/JRMCPRKVXAXSi/5gWWROYagkRn32An8MBG5LeEnw7EAX
7cj6v3eebjGtu/Jp1ySDcy4syOB+37L3G09nly9e070MO6RwZ9DRLauSwpWbTzkajobe3d0E2+45
lUQVX1Ppd+JIvQtgrk4Gk5tuKTbFBDEKh5xGNyinUvr5KSbw3zlMNsSaCGi9h+IKmtEnoR3eJKx6
0HWqG8S/mGWRxis0R/4KhoMr9Vt4FVPxkmZ86SewI+IrDylvmk+l78srMjDwxThTtynhiQesy5Y8
bbUiD/uJeNOTLRG2fGCvJG3pbmGWTcQuGj36Ee66fSB2v7tQ0IQuLxaur8Hm5sFp12KPkXjr8uxp
d9iKYbNrhffuvHyKmgc1oRFTab7C2tclF42IKDqVQ6tXxja1Rx3drhKpWEdcOZdQR0vFWwySKr1K
fVG9DqOGW24Uu7i4fk8Oln9GWAOfTQBxcw5aXB+Oow0LxNgFfs5UqFB8Q5o5Use9qLa1g/5RwlYC
OBKBgoDRN7nPgF5oAKiB4J2G0J3ZTFSAQhFB92M+KwjTLajCBBCjB9yQ+eHM9JFf1KpuiER4BIVb
gNUB1Vwo4Rk2WFz1BChz0w2B61Y4wENl8JGGWSXrXjjbd1/Xf4+0PoAlaKJ006Tpfv3kOovg4zWb
4F/Vskpd4trIrqK4tqx2OTPWtju6ZSUC+34GEw53Wm1RURnwyUceh0LWqx2msecAyY10OHW0vPj5
2phfL8oW8/fapWqI2QPd7gtm/T917KmzdKq6TOruDvDnq2RZBah/weIgeQVRV/h8Bb4U+4h72DN9
MYk2A5Ti0Xr88mcKcuPPMB8PU5+vOJ0LMKoWTAm1VY6WAEAUoKlUsxdtqOMI3bx0me9XYKgri4AJ
BG7Wy/ziEFXTzDhSLn4Z7QscC2f4GR9rpIUYKCKrY+8HzTUB1z79CmsKUI94zjA5tWg70SJsmz4T
XzoLNFy0EZuPaAfhNLm2xG6xj+L+ymY0r4qVPW4WW8t/yHyKbLLVthoVlAuU6yqoO7Q79o6dtNtU
UBeUl25XlQqq3l028uxoGSonCVjuYgwqAv67LT2rtIheeU0qBsONvxFKOWmuTzIDP8NAV1ug1N7R
NRhobFDKkjfdCl15uLuZhe4zDHSBf9XAJzu/ojEG3dAKKcdp9zBOnftP9NrwE/9r6VYT8HMUFIBv
d/q9/Y7XXuB3Rt2hblcV8O+h7Xmwvfb544xXc632tz6WYH3JQUXVK2gopXDpAWeru9f0rhb8RYEQ
YOn1tdOg05poFQQi5c0TA7z0OGsCVL7U1wA1GI46g/bhCc2DYb870u2qAtTtmvS3/wfFwB6d8dEA
AA==
headers:
CF-RAY: [24e56974010611a7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7639']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:22 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['585.0']
x-ratelimit-reset: ['278']
x-ratelimit-used: ['15']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=x7Hgtcci0o4lZvWWI68HSCYaPfSu8AoLGJymDPfSvXaVLHrXHOY314V%2FS7aiBGPeCNk%2FdOqa1ik%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,339 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit.json
response:
body:
string: !!binary |
H4sIAHihXlYC/+1djZPbtrH/V+Brp2dPdZJIfTuTyTixU1/G+RjHrV+endGDSEiiRRI0P3RWOv3f
3+4CoHSUdHeiSPnqXNuZ+igCBBb72y8sFu/+fbbwQvfsKTt75SWpF87OGuzM5SmHR/8+C6Q758kc
f8bnztzz3ViE8Pe7dcO0c62NKwPu4StnMy+dZ5OmIwN8YcLDULjjyQp+CjPfh0eBcD0+FsFEYEf/
/g88SrJJLFzXS7GDX1bpXIbYOBH+NBWf0vE8Dfx1B+axGaDvLUSy8XM2m4kkha8mMsaX9PMsEfE4
FhE8xLff/U5dOVksxjSk9Zu+Fy7GU5978Vh/R//g0dTtT4Hs0/SnsQzGmiL6lRkQiybWhj94DMRb
0p9pnAkkpu85C3ow5X6CT9SIYGA8keHGNHgGZIjxe4639HzvD556MhxHc/6HGNPXC6MOeSDw9bQz
Xo8wcWSMT+1eH/uMolguC+sBD+KxNdwY0txzXVpv8wBWNJiE3MNFIJLnCzZWJEl7Y/vjvL3C32BU
6bUZbpDTSZKx4/Nka577f3flFZEF6QmcedMKFpiKK8qvCR3IJfc1pdcfAAA4C+/aq7iu6xe8ZIws
V/hdzVy/Eok44DhPJEYrbikebgEIAhGmSUstSIuPI/oB2CoOvBAGs/TEFZB/ChSYxPIqASyOFWVb
xNqywC2wNGJsFtU8dGBKiuRW1+7Zo67Vt5tIryymFZunaZQ8bbXW0GwFnjPnwr/w+R88bsXpEr92
bSmuc/7HjMc8BFGx+d3US31iuWdMzYuZeTE1LwbzYmZeTEN8PeBxljpm0P1exww6wsVVDJtFS5mK
cYy8Dw/bzRE+DbNgbEgLT7tdeLb0kgLb4VtrZjHcpORd5iVzehkf/+c/yEgcZArCTb84EVNFZPVG
g1UhM61rbW4C0Q65uY9/CV66h03oF8QikML36MHDTB5m8jCT22dCI9xpNRQ0i+rUiezRqpNho1uM
gNtV/oS7oNhSGSfYXcSBRPnsrTF8aZgNJvhTrt+x+S7t7sgwxadx4oGCSvEXHNREuvjPs99EwtK5
l7BkLjPfZVcyXrAr0BOM+z4DhcGgzw8wlIQ9fifTOcj0SEaZz2P2r+9+ZTAwlmQRTke4jCfsSvj+
74+Nwom8qBmtIt70ZEuELR+EfpK2YjEVMCFH4O9jL0xS+FYTbby/LJ3kQvf35Enzffg+vJyylczg
q9xZ8JlgrhczHK4MxBWMRrCJCEGZzGEWgiFhWSxl2sBGLPBm85TNYbVYKtm7D1kQwWuxzGYwPTYV
VywA6rG5lFFyxKDFJx5EvkiesEmWMiAZji/1gH6RTBJv4osmLtWWXXSI5YPrZczgs7/56Veut2T0
/tfvzwL3/dnfZulX+DzCfxyyqtiIsznMD3oqTYONhVNjuSOv4NdbHBs8adK/afy48PlkquOAaqZq
llvN80amWs9uP2tszpr+DSuLfxG69/hFCPrxlpWeOwAkIYwsum4cdvqdTqdPdtYN1t62fdbpdHu6
3S4TqrHTNt9pgWnz7m5G12Hi1wjFo8XvL9wHQ1b+IOdE7235a8UrmxTbsfLXlQaqhFG0l4sQ/ebE
wuMuQ6qLac0KFpnW6vW7ZZjWHuh295Zpc046mmlvsxmsRd+imEzOs+g3lWHaf4Ij6AOLcBStUSTC
pMF4uJKhYA4PtYDTMhOF3tLjDMToUxTrDP7zV/wrfwEY6+938VCbM+U8nhAKZSe6Q5PFwvTsSJf+
XZ4KGnOmI/U19YFK4ZjzZgGOHas3bJeAY9se6HbVwLFbBx4NSo7Go8uXEjhrKffAcd4fuJtwtMrq
kJdi9YjNkNxGQDfYFfIsn0iwOgK+wOgLiu81v3EwPcCEYpGQYMsQN6fxCl/CJl4IpkwS8ugb5N93
r4UvljxMWZqBeIExKGMZjScBpCd8EbeCUZMmrakXJ+lF6gXiAm3OC7CuPDKdnrwn1jshgOskzQ6Q
m29fMzIPJpOawxbR6XtkTNakfXPWL8C9bbfb1uFwtwcdW7erBO6WfT0Md0+iBH/ieIclhzNeiazc
t8WxQ2zKrow2xWZZqflmzsMF6PPLc9C+vpQLgDb4iGT4phJ8uFhendpvz8f0N3Axv/pLZ/TVDWOr
SwqYRS1IAas7sFWA/jApMOyNdLtKpEAtNrjhqaP5+J0LUhsI8Pse5i3q/LLMe+07J2TQ/Lu1MZ9e
iS3ms3u9UszX1e3uL/MZnjia+e4mRHfuDlvYUQk+fOlhbKBBgdrzpWARWEipnAkK/oGdFKBro/cG
tQGm3Kkpd2CsKQbb1huEr4lrNqJp+XbiFWhKEHnsMqWgRKJtOoxNIA+AU+ayjyQ0OZvwRLgsZ8IE
WoGh5wolPifC4bA88OwKjCt8UQYwXPw8DeySORQy5WnKnTlGDWEYQfaJJSJJgLD0qYUQaC5SxHHC
ncUMBgyPr0C7CxogdgfvqiAo9U7DmNJriRd4GBRdx2BFc9Zkz0ANh98L4T5pULQQ2q6Y67nheWri
pmtSGRrBNANPDczsiKKFC14jhhovf/yZxhiAwcm8ALtAixIYIn2EFKCZ+lJFR2HV0LllUxgCTkrR
A6Oos2yVbIRST60VNZPtsbfXuvJ+8N+XyYM5ke8bL9aliIxULuZW9AadQQlF1O+PdLtqFJH14As9
zORhJg8z+cwzOThmYC8+WXNsdCJzV20r/ZEM8Kc81IodlTB3X4ErnjAkBqkyUHYZ6FqesFgk0ofO
UCF3mt2mhfsELqi1OQc1HiG14P/ZxAf/HlTb1PtEtgO8yuhjMEWl4kQIRGFZBD3CgqrwJHbYaTIV
HKB2tB8+F370KLe8ZzJNBcYmcQc6Fh8zkaTq5aVEDqPxrNUyRT0CDhPhoZ4HvuspUwbjnaCFad98
bZuAUeM5OLZEyrB58lju/SP+XSzSQ9dlM/Jz1PrUZBjl+C146EN7OCoRJO70um3drhLDyKrBQ8/F
x9Ei67UIhMv35BTYbRmQUD9WSiHjA2OodTUM/NbY4VfCi90GOAChg0DimJOC7IVhRg0v4LJduGqr
hCwjbTjjyYJdXFBiD5rmEQAsJIcAHBbXBY7naQadgGMAn4YOtzke/0S21gkHM6mbTwT7kIGfwGEQ
4DbFp86BqI+Ed5UZJ6VuXbLCAGcrnWPYHpWQFZ1OW7e7v7LCgPhoWXF388aKlh6leh0bVX6DXAL/
A12ZzUxsQUdPEvDWXR6Di+5NYh6vmux9qHc34eWkqV5rynjWIjDYVns4RBZ/LnUmHvClNwWtjHyN
HU9hTEYBwwCDiYiZnOYnCbTDfw7sn2GY4dQi4FBiYMPivu8NlKGP3foagXFrz3dDbhxJ3Vzq5FSu
SxQYXBREQXc4GJVJJbFGI92uElFQS2DfwPJoUXCj2WBFkUMpJgb9HWxbAv3/QCo3SHNRiBPtzUCw
EOOe36pYYG6oklYCZMDLgUgw1plFTfbupYjFOUImgSUTYTKXaTPPFfGaXjDLYsqAmIre/1o/zJpR
OHtyYlwfP81dSN8zOfVtJEuOtAJ5diO8QuTlbFhAnt2xrBKJwPao29HtKkFepxbkaUgcjbwDlLBc
dpxNGA6xoxIwfClWDZTPxt0kXzecSgySg90HPIhP/nX5I7Bl6AL9E/bVEzLMyeKk7CWw8WIM0Tiw
eMr2E3EMfU1WLM6UFfn+zOgykWaYAW/Sn96fKRdzDl5rIJdkMprEetqJcL0p5canYO86RBnaF8H8
SB5rT1qZxngkGHuGceEmltahcbpswcxgZWKCod7iwpA/tPIznKJ+FTnsQuf9wxxmM3TEvfxYA9ix
4MzD6wDeBhjIsI5ghAsEK1vAM9SIBucwxvNAZ2zCi7EypeEbYcZhrAkpR33CIOAh/B9KgJNvdZVZ
/D2mwWH8QPLpYyah8W6+WL9QC39sbDDdwCcbb9XPL/nHSvBNbcJcS7aiMLftMh6VPep0q/Sohvdy
V+rgGLUVjWb9SvTHm7n49ts7Koyy4Z7fRNQA8YB4QL5MgF8UvJvsVwFtUUzQL0svyMVGk71+8avZ
ykVQigDQK08t7yode22Y09xQwJw1GtqdMpiz2rpdJZirI4qR8+bRANjruuxKQ9pbouIWBFyi4wuK
jDhlivkPFKM3+dyoglB7Yu4kC1YgrtN5znRGF+aHU97E3BGUc/CYUhVi4aAuczCTBNg4ffJUvcfY
9/il92etLIlbwJstUFjvYTognwQbNVAnIRfCama+Ppuh2jFQjNwdQ6/xahxJL0wfn0Pbr7+2mm1u
nTfYOcw3kT4Weoi9KE3wEbxw/uTxk13f9r1JSyntTrPbwjIIuQ5sRQusJ5EAOUBXtcZjLwTxOwal
m4+0h8lfMNbimNajjcESiEOk1xg5NvYmGZL0Mf7xpFls9xgTUQDViKEahmv3O/Yt4zUDFhENrg6i
gfO0HsUWpbBaSFOHfmv6+pC+rr+xMXXFbexrBu0ox2Y8fkzDUT+McVUaDOuX+LB6X787H9Oj8fgc
5IEvlsL/um1GfEkdvCB0sJ+k6Rvfd9k5Gm/n5HcY9BlzDnsnKDq+BKWuTEOwqxIhAsKgOlcIM1Eb
CsVYO2YtPQNZw155YfaJ3nnx/PLNU/Y9mKy43ZWnBT1VCVnatgMBGHtTz0EzDoNiymC+ZiyjTe16
AGlk4IS29ehM7rz1obVo+RhG98U0baFGa2VRK8ZEo1MrxUrE2Q6nYPsI2t1kHTHu2vzflHfrp9fl
Hj5eyz78K5d/Rdw+XscgcxmYP2qorvCfBZm46xUzIPy3lpW7xl4KfFszvVluHikz6xv2bfJzl+ys
cTTbcnSvDK11FLvk6aGyNOc9I1PzB9dl641yNW+D0jT/Qxu0u098bvr8R8jiHRKDurxFPO9p9nkl
dl2ugLGLi67AYNApdTZmONLtKnEF+l+I+z1fwHQaFXgfP0x4HK+6/d7d/I+y5+HNVqEjpd9sNlXy
0RVGjKa4L8cZJnur9J0TmxIHjKwuyJjFvA4Ze9TvlfKe+4NBld5z9wuBTGQvRCWQ6fyj/6z3ttMZ
vrgbZsruNv7kOQI4knmu4D6xILIiV9FX9vQXMvvfSMkm3GVXdAz7PMWqOakqZOSd3Cy/dcR7NOH1
SeQqvTCZuuBnGKOosYZDu8yBulG7rdtVAr/OFwK/cOZVEzB+5aWpL15EMsRSm427ILBsts+zK3X8
OWBOFuNH/NXmuSFMDkPas+9eYVYJVetSO+QuD0JySROd8Qbz8aFxCAChvSF8FfxHz8WKCU08YxSR
rQltHrGnp97w1/NcG9annm9d0DZMV4S21euWyMS1h/ZAt6sE2vezWsPh0I6SD9NKoO343sWHLFx4
NeP6H1Kqin+4s4JbwMi0yOSostbZ4jqR3qOdceDfv1sqK90L2EKs1g6YgcJhqCU6l8dsiTnk+L7D
XOpCpOGVrd3Z/mD/7uzZdzIIUIy8wlgEDofYYAdCR52h7ucBoRsI/Sjcaqo3/Dr3Yjn3ZjUD9JJq
CoUyZSkqDgx8zOUVCzCP+5LREVh8RpEPlbJNHP7jCnRMlMoIHTpqjfuiIgb+FpjMTb4dRknxXC2o
pwTmPZv5QpmYkYihwwBzFlBBTXwR0OlcERolSBb3TzAQNT7Hl2je+lj/DP+ltWPKJwnTMSGVUA66
U6dfbBxDxtHAQDB7hLLp8G+MtTXZTzByGcL4dJ1BMKDdmAc89TDsDI8Fcj58MAANK4RLdOB+egFf
VoeSYf0phjWNBfSQRWiHu+z1sx9ZAEwYr5TrgEk0SEedT1P45+nNkPu66nu8li+CEfbM7XbeqEtB
GFFVVBC9/mi4V0HsN9lG/aFu9wUrhD/xYVkr7i8+YKPjVVsqePALWJ/j52Lp+djnjpSkuOPbOS+f
lU9JessBlYRZ8IKyaBZj6QvcqlSbCE/pbosTyt5bx1MX3s36FfE+HHVL5F532m1Lt6sE77WkDhke
qp9lq7TJnkulhjxdLZyDRp6pzXdwImYiVWfrmBODsKGqmC9QCVL9cY9y0NRPzFR85EkqwO+gAwET
+9vvXzm/0M7RCbn+sDnt0ZV7pom/F89A7JzztYNOO9+gD9d6AsKw5BYK+2VKoduj0bDiUuj3UOse
rquSmUf7bEcDP1mF7oS0fH14x23k8wRsYkABGM2es3jEKOjwQU4azM1cnZuUYmjEEXHKPTRV8dg/
ZoujFatvOFBVYrMUz4moygT/52RxIpL/M6cDlSlOuTm40Z4XosLs/JDp6wQOjK8cLRyQAHnI5GZC
7JEMl+v2h9MIezHZDIpc9BXz6DDS1SU3DEcX5EanY7VLyI1Ou9vR7R7kxobcSAf9T5XIjR/FYs4D
r17BcanKnKWwtqA6I2jDdFWP/MAOaLkEvGsXy0RPs1MD+/AB1gUfs7BF+PQACGXg07d1uy8YPn9i
Z9e22na7EkFwwKnNTMwrOYTzbJJIP0uFv3rELvHgsA/jc1d4DdCCzqeB4/nL62dvcT8D9y7UIf05
j10EJgxsIysOKzuqACAe/gPfbModTOATXFV+TLzPYTB8hhnWJJhyRisIpu7ILhOF69jtbpVRuFq8
csPnR2MrAgSILGkP21TZpT5F+0zXvZGRCDFsA6sg1HkwDD6rGjjrc/qhOhmL0ugbRiWKQgFdU5Qb
uJPMyFAZmip2fLlhsRJfYnoRldFI4xXxpsSwusrcFk329Pmp8XbE/HPDvAwdNjKUbqRHTejMWbVo
NgwGN2yi7kdnezh62DTdFggrV93gfbRAUPOVsWW1iRb1SQSTzEoXuFxSPPfUSnDXEOrCgVmhopbq
mQy8w3Bg9e0qM/fuJw7+xOazFVqCmOtoRB9gPgcfOKWgGWiXPcH7ks85S2Qcg3H5vRA+bemSypkL
Z0EXL6F6U4d58BAIBqQ5FefxcRt5qk1GsjxnejdZFVFPqHQDGJ3qcAmlDNG14vAinRrZqJsHBigV
tDB6tcnU5aPMlWgeKLMW/1ZdTzIPP5CnI9Fhm8SJeerMGyxVBUSxlEb+BhWkB3WsE5c4qlgsZY6V
KfDAH0bsH51Ypv0X0H7DJDnRGtQl1A1IC0LdaretEsaNPbRs3a4Sod6vw/UwMuJoufQarNRVIGFG
/3O5LY+qtDR++/mf7Nc3P796wX78jV0+f/GM6iz/gDUzF7C+QGeVGXxpUoGx0jBFqfFwgLKrzwNK
1+FshtF0EdIVCTCUWcyBwWPkM4INMjseAGDPrgQyKZb6mYPRvyF3yAznmNrDwxnF0wWxtarhhwmR
CM1H7MSCY5tKm6jZ2Ck4jHAbidqVELDoz9yBkHWh30ChgP72sNSdzPZgVPWdzA8mXY0zOdiks63o
YzV3Ax9g0n20llebMrRsRBTT+569+ZEwPd00K+jeGNS86taYtW1BZgDVPE+y2UwkdOL3xDKtolHX
JEByhigIkF67a5epomqDU1hhFdVaIpeGH4/GwMVPnnexzfS77IayPP8bJvViqs0U7F7af1tKj6rT
vaYVp7NFDogDjNmR6WmK9JGVjZYnC1ZUY2BDm1HeTawq/aOhTjZ3KuFdqS4iMNdIYRkC0y1m7gKP
4xMM+p26evI9pkRN2MwZtYBNu1sqXmOPepXGax4ujHqYycNMHmbyuWdysBFsJauKbl/Zmx+kkr2G
8todUWVjBx44/jMZhhxr7FBFWdJA6i6fXfk44PzOMrrPB28mor+piA3jV3x16g0Ob8P7rnAWdelc
wxvFvcJut1/iCHSn3evrdpXo3FpuFTB8ejQc+AfgglhVh9iFB3tAUuZYm3gzG4SOrKlza1gGHzel
uTomRhWZ13WhJIOuuE44V6fG1KVe2tHCdHQsA4XRMYrc5CltdEgsXKlQrwDaNU8dFqt3whvRrFsn
XhvqNAsWUWd3ByXuRu20O33drhLU1eKFGjQcjborcFMWwG1zPJO5B3rxKhxWoYqe69MfxEPIPpij
RVlZQGcZr0zZtciL9E4I7Z5wOiNyao/xuMHWxup63Yusbo3KRGyB1a2KI7bVs7rhvqNZ/RYFE8d/
0FnY4xWMF/AZng7w8EhHKrC8Il3KlAchKJ5AFZ5fp0t2keFB4RgXml1EuQTFn9UVb+Z+7pW6fsoL
o0ydlZLqePPmOWbMBvkJj1j765vA4RGyLGVFXmNhkTqfQR2tybNx4GM/mTahtLGJcwPl9rTYImb+
+eqJWhf+DRi28N/vlAm4tu2hblcJ/mtRdQaXR+P/Zn/rYxZfg39ZJfdWchWOzy0hTxVKUCuL7AWf
jlffqFAgGIx8hjF9fbIIqw4Ix6Nr0k6MzANHvmH57Z1BbTDQPLEFg3anRNoCwMDW7SqBQS1q0PDn
0TDYrwar3Hp4ozMTuQqWNzFY7lGNwcTTVwDhkODr5hagDb6jgiBoXIlPwslSunuHYb1xwV0Ksa8o
XR/6o0sCQfbjjYc6IyKR5oCfghcGDKgsue4cx4wP1CU8Ij65Bjw5afbow21qbUD6dqrVhW3D6MV9
i0F/UELF2aPhULerBNsP+xb1zuTgeKxtLfrV+AcHJCXE0zadxD1WSq5jM3T8m7MZMjAlHzUK162D
lTkDBZtNTp2PuTnGDYP9bmOtSUjki76VeGANuocLiY5tdXS7qoRE9QaA4bmj+XwRryJQLWPMPtvm
72qNgI2bA9cazxybpyzGy41zeIqjUP+5KoeWoXRCtebKFL419XS9kMe+BBeykaunBpicU575KR42
VkzVQNUmwyfGPZtmeD15k719/tub67Ek4gCr1xvtCBZWiqTbyLFHT38WCtUE3JyLi9p9NBiV2iFp
j3S7qoD7oN0fZnKHmRxup0z8oJqQ/Wu5ghm8BOvdB/XSKApw0o/9cEQVqo6V4G/VOU06m8CZM+dx
YC6EfcTorq1CWjgWWklgrWYNOpOBv5L5gL6ISqYCyUV/sCwyZy5U0qI+aYE/BgI3JLwk+OZA54yW
pbyEvvN0i0nclU+7Jvmbc2FB/vb7lr3fcDq7fP6K7l/YIYE7g45uWZUErtx0ytFwNPTu7iLYds+p
JJ74ikq8E0fq+L+5IhnMbbqN2JQNxPgbchrdlJxK6ednlsBz5zDZEOsfoOUeiitoRp+EdnhjsOpB
16NuEP9ifkUar9AU+SsYDa7Ub+GVS8XLmPGln8GGiK88pLxpPpW+L6/IuMAX40zdmoTnG7ACW/K0
1Yo87CfiTU+2RNjygb2StKW7hVk2Ebto8OhHuN/2ntj97kJBE7q8WLi+BpvbBqddiz0G4q3Ls6fd
YSuGza6V2Lvz8ilqHtSERkxF+AprX5dcNCKi6FAOrV4Zu9QedXS7SqRiHRHlXEIdLRVvMUiq9Cj1
hfQ6gBpuuVDs4uL6fThY5hlhDXw2AcTNOWhxfRSOtioQYxf4OVONQvENaeZIHe6iGtYO+kYJWwng
SAQKAkbf2D4DeqEBoAaCdxdCd2YbUQEKRQTdg/lNQZhuQRUmgBg94CbM92emj/xCVnUTJMIjKNz2
q46j5kIJT6zB4qonQJmbbgJct8IBHiqDjzTMKln3wkm++7r+e6T1ASxBE6UbJU336yfXWQQfr9kE
/6qWVeoS10Z2FcW1ZbXLmbG23dEtKxHY9zOQcLjTaouKCn5PPvA4FLJe7TCNPQdIbqTDqSPlxc/X
xvx6UbaYv9cuVS/MHuh2XzDr/4kjT52lU9WVUXd3fz9dJcsqIP0rFgLJK4W6wucr8KTYB9y7nunr
R7QRoNSO1uKXv1B4G3+G+XiY8nzF6TyAUbRgSKgtcrQDAKAATKWYvWhDGUfo5KXLfKcCA11ZBEwg
cJNe5teDqOplxo1y8ctoXeBYOMPP+FgNLcQwEdkcez9orgO49umXWD+AesRThcmpBduJFmHb8Jn4
0lmg2aJN2HxEOwinybUldIt9FHdWNmN5VazscbPYWv5D5lNkk6221SigXKBcV0Ddod2xd+yh3aaA
uqC6dLuqFFD1zrKRZ0fLUDlJwG4XY1AR8N9t6VmlPfTSa1LhF268jVDKSXN9bhn4GQa62gKl9o2u
wUBjg1KVvOlW4MrDfc0sdL/BMBd4Vw18svMrGmPQDa2Qcpt2D+PUOf9Erw0v8b+WbjUBP0dBAfh2
p9/b73btBX5n1B3qdlUB/x5angfba58+zHg1l2d/52Ox1RccVFS9goZSCZcecLa6YU3vacFfFAYB
ll5fLg06rYlWQSBS3jwxwEuPsyZA5Ut9DVCD4agzaB+eyDwY9rsj3a4qQN2uSX//f8PoJAy/0QAA
headers:
CF-Cache-Status: [EXPIRED]
CF-RAY: [24e568cfaf801e77-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7638']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:56 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d74b6c79ddc42876e5cf9c8b498e180681449042296; expires=Thu,
01-Dec-16 07:44:56 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=C5eD%2BuAYBnEBQDixycLxKXNrKiWD6EyOaGFoO741H%2Bsy3o9p%2BEo2aG12QvmumoNBmrd07CEiKS4%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=d74b6c79ddc42876e5cf9c8b498e180681449042296]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit.json
response:
body:
string: !!binary |
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
headers:
CF-Cache-Status: [HIT]
CF-RAY: [24e568dacfa21e77-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7638']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:44:58 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=C5eD%2BuAYBnEBQDixycLxKXNrKiWD6EyOaGFoO741H%2Bsy3o9p%2BEo2aG12QvmumoNBmrd07CEiKS4%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,340 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/2xmo63.json
response:
body:
string: !!binary |
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
sr/RAAA=
headers:
CF-Cache-Status: [EXPIRED]
CF-RAY: [24e568e8522f11cb-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7643']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:00 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d963647719488322ce24aaf79235c3d6e1449042300; expires=Thu,
01-Dec-16 07:45:00 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=6lIDiYZTMgGMwfj8vOpnR3UYxhGFIIA9aSyQodiHoi%2B1VrKGsd9Ub3VR5u7SCY3M3kRksUJzoq4%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=d963647719488322ce24aaf79235c3d6e1449042300]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/2xmo63/a_python_terminal_viewer_for_browsing_reddit/.json
response:
body:
string: !!binary |
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
headers:
CF-Cache-Status: [EXPIRED]
CF-RAY: [24e568f3126611cb-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7637']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:02 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=%2FI%2Bt4RjqqoY4TKM2Yom1PshslaAKuc8DxzlgITnz0cBvO7lleFhmymqgoKcNsIvy8kysr3udmUc%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,77 @@
interactions:
- request:
body: !!binary |
cmVkaXJlY3RfdXJpPWh0dHAlM0ElMkYlMkYxMjcuMC4wLjElM0E2NTAwMCUyRiZncmFudF90eXBl
PXJlZnJlc2hfdG9rZW4mcmVmcmVzaF90b2tlbj0qKioqKioqKioq
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
Cookie: [__cfduid=d963647719488322ce24aaf79235c3d6e1449042300]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMsQrCQBAFf2W5WiEhKmIpio0iCBZW4XL3MIvGC7tr8BD/XaLVg2HmvZ0PAaq1
pRsebkVuNlssi7KcT9OhPLbnatNV23jfretLu7vl/cnrKbgJuV9QW+4xVg28QEaOV88CrXl8qxZF
MSGnIf01RDZqWS1JJo54GFumLuuzEcTIptQLD97QQdVfoSTwkQR9EiP1A0ifTcc2jgbhBjQkg/t8
AdrN+hbKAAAA
headers:
CF-RAY: [24e56900c7132888-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:04 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAAAAAAAAA12QwWrDMBBEf0Xs2YTYuLTxZ5TSq5CtdbNE2k1XUhoI/fei1C5xjxp23ozmBkeX
bHQUYDCzCwkbA+wiwmDgFb2n/IYaiV14J/xChcbApOgyehhM2/fd89O+Oxx2+8bAkTzaWSValVFy
emBSsqmkM7K/G1d5QdmSpxXXtf2Kq9XE/68XiE/25DS6aqkQiRE5/2nVKxdU275sG3xI8Fslykao
D/gMjGP9Zz23eD2TukzCMBguIdQ7HuVqJymcl7ha9YJKM6G3uBTOWnDFTHofMy33v1skEn4IWFao
Id8/dZfx5JkBAAA=
headers:
CF-RAY: [24e5690dd21e2834-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:06 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d69f2e4e0722b7a1abec4c70f0a9db42d1449042306; expires=Thu,
01-Dec-16 07:45:06 GMT; path=/; domain=.reddit.com; HttpOnly']
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
X-Moose: [majestic]
cache-control: ['private, s-maxage=0, max-age=0, must-revalidate', 'max-age=0,
must-revalidate']
expires: ['-1']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-ratelimit-remaining: ['591.0']
x-ratelimit-reset: ['294']
x-ratelimit-used: ['9']
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -0,0 +1,181 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=dc348748e62d1cb7fa69806dc4fc6991c1449042326]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://www.reddit.com/r/Python/comments/3kestk/post_learning_questions_to_rlearnpython/.json
response:
body:
string: !!binary |
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
9I6qwLQAAA==
headers:
CF-Cache-Status: [EXPIRED]
CF-RAY: [24e569cb3b081213-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['8215']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:36 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [Accept-Encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=QCGURJLJvc4THMjF%2F%2Fvx844ubSVYYPJP%2BL%2BgFWarGm%2B2i2o4igfcGgf69xpsnqgo9A7iLkVNd9E%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,987 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=dc348748e62d1cb7fa69806dc4fc6991c1449042326]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/top/.json?limit=1024
response:
body:
string: !!binary |
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
headers:
CF-RAY: [24e569bc645c1ea7-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['54243']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:35 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=g9dPP8ZNmgatiGERe2FC0zb0NCd%2F9Zd5mmZ%2BBnbF7NAeZQs%2B5wxtcOZxk0xvgAqYNNiS3%2BncbHY%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,298 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Cookie: [__cfduid=dc348748e62d1cb7fa69806dc4fc6991c1449042326]
User-Agent: [rtv test suite PRAW/3.3.0 Python/3.4.0 b'Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/python/search/.json?restrict_sr=on&q=search+term
response:
body:
string: !!binary |
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
+9//AwDluVgqLgEA
headers:
CF-RAY: [24e569afc5810294-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['14946']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 02 Dec 2015 07:45:32 GMT']
Server: [cloudflare-nginx]
Strict-Transport-Security: [max-age=15552000; includeSubDomains; preload]
Vary: [accept-encoding]
X-Moose: [majestic]
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=onmi2HlExxjKlqXNeMPk33hyjq4k4I7oFbFEuSE%2FMBpTWdwvMnFq78kohzkD1RIsStVVrl7soRc%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

221
tests/conftest.py Normal file
View File

@@ -0,0 +1,221 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import curses
import logging
from functools import partial
import praw
import pytest
from vcr import VCR
from six.moves.urllib.parse import urlparse, parse_qs
from rtv.page import Page
from rtv.oauth import OAuthHelper
from rtv.config import Config
from rtv.terminal import Terminal
from rtv.subreddit import SubredditPage
from rtv.submission import SubmissionPage
from rtv.subscription import SubscriptionPage
try:
from unittest import mock
except ImportError:
import mock
# Turn on autospec by default for convenience
patch = partial(mock.patch, autospec=True)
# Turn on logging, but disable vcr from spamming
logging.basicConfig(level=logging.DEBUG)
for name in ['vcr.matchers', 'vcr.stubs']:
logging.getLogger(name).disabled = True
def pytest_addoption(parser):
parser.addoption('--record-mode', dest='record_mode', default='none')
parser.addoption('--refresh-token', dest='refresh_token',
default='tests/refresh-token')
class MockStdscr(mock.MagicMock):
"""
Extend mock to mimic curses.stdscr by keeping track of the terminal
coordinates and allowing for the creation of subwindows with the same
properties as stdscr.
"""
def getyx(self):
return self.y, self.x
def getmaxyx(self):
return self.nlines, self.ncols
def derwin(self, *args):
"""
derwin()
derwin(begin_y, begin_x)
derwin(nlines, ncols, begin_y, begin_x)
"""
if 'subwin' not in dir(self):
self.attach_mock(MockStdscr(), 'subwin')
if len(args) == 0:
nlines = self.nlines
ncols = self.ncols
elif len(args) == 2:
nlines = self.nlines - args[0]
ncols = self.ncols - args[1]
else:
nlines = min(self.nlines - args[2], args[0])
ncols = min(self.ncols - args[3], args[1])
self.subwin.nlines = nlines
self.subwin.ncols = ncols
self.subwin.x = 0
self.subwin.y = 0
return self.subwin
@pytest.fixture(scope='session')
def vcr(request):
def auth_matcher(r1, r2):
return (r1.headers.get('authorization') ==
r2.headers.get('authorization'))
def uri_with_query_matcher(r1, r2):
"URI matcher that allows query params to appear in any order"
p1, p2 = urlparse(r1.uri), urlparse(r2.uri)
return (p1[:3] == p2[:3] and
parse_qs(p1.query, True) == parse_qs(p2.query, True))
# Use `none` to use the recorded requests, and `once` to delete existing
# cassettes and re-record.
record_mode = request.config.option.record_mode
assert record_mode in ('once', 'none')
cassette_dir = os.path.join(os.path.dirname(__file__), 'cassettes')
if not os.path.exists(cassette_dir):
os.makedirs(cassette_dir)
# https://github.com/kevin1024/vcrpy/pull/196
vcr = VCR(
record_mode=request.config.option.record_mode,
filter_headers=[('Authorization', '**********')],
filter_post_data_parameters=[('refresh_token', '**********')],
match_on=['method', 'uri_with_query', 'auth', 'body'],
cassette_library_dir=cassette_dir)
vcr.register_matcher('auth', auth_matcher)
vcr.register_matcher('uri_with_query', uri_with_query_matcher)
return vcr
@pytest.fixture()
def refresh_token(request):
if request.config.option.record_mode == 'none':
return 'mock_refresh_token'
else:
return open(request.config.option.refresh_token).read()
@pytest.yield_fixture()
def config():
with patch('rtv.config.Config.save_refresh_token'), \
patch('rtv.config.Config.save_history'):
yield Config()
@pytest.yield_fixture()
def stdscr():
with patch('curses.initscr'), \
patch('curses.echo'), \
patch('curses.flash'), \
patch('curses.endwin'), \
patch('curses.newwin'), \
patch('curses.noecho'), \
patch('curses.cbreak'), \
patch('curses.doupdate'), \
patch('curses.nocbreak'), \
patch('curses.curs_set'), \
patch('curses.init_pair'), \
patch('curses.color_pair'), \
patch('curses.start_color'), \
patch('curses.use_default_colors'):
out = MockStdscr(nlines=40, ncols=80, x=0, y=0)
curses.initscr.return_value = out
curses.newwin.side_effect = lambda *args: out.derwin(*args)
curses.color_pair.return_value = 23
curses.ACS_VLINE = 0
yield out
@pytest.yield_fixture()
def reddit(vcr, request):
cassette_name = '%s.yaml' % request.node.name
# Clear the cassette before running the test
if request.config.option.record_mode == 'once':
filename = os.path.join(vcr.cassette_library_dir, cassette_name)
if os.path.exists(filename):
os.remove(filename)
with vcr.use_cassette(cassette_name):
with patch('praw.Reddit.get_access_information'):
reddit = praw.Reddit(user_agent='rtv test suite',
decode_html_entities=False,
disable_update_check=True)
if request.config.option.record_mode == 'none':
# Turn off praw rate limiting when using cassettes
reddit.config.api_request_delay = 0
yield reddit
@pytest.fixture()
def terminal(stdscr, config):
term = Terminal(stdscr, ascii=config['ascii'])
# Disable the python 3.4 addch patch so that the mock stdscr calls are
# always made the same way
term.addch = lambda window, *args: window.addch(*args)
return term
@pytest.fixture()
def oauth(reddit, terminal, config):
return OAuthHelper(reddit, terminal, config)
@pytest.fixture()
def submission_page(reddit, terminal, config, oauth):
submission = 'https://www.reddit.com/r/Python/comments/2xmo63'
with terminal.loader():
page = SubmissionPage(reddit, terminal, config, oauth, url=submission)
assert terminal.loader.exception is None
page.draw()
return page
@pytest.fixture()
def subreddit_page(reddit, terminal, config, oauth):
subreddit = '/r/python'
with terminal.loader():
page = SubredditPage(reddit, terminal, config, oauth, subreddit)
assert not terminal.loader.exception
page.draw()
return page
@pytest.fixture()
def subscription_page(reddit, terminal, config, oauth, refresh_token):
# Must be logged in to view your subscriptions
config.refresh_token = refresh_token
oauth.authorize()
with terminal.loader():
page = SubscriptionPage(reddit, terminal, config, oauth)
assert terminal.loader.exception is None
page.draw()
return page

143
tests/test_config.py Normal file
View File

@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import codecs
from tempfile import NamedTemporaryFile
from rtv.config import Config
try:
from unittest import mock
except ImportError:
import mock
def test_config_interface():
"Test setting and removing values"
config = Config(ascii=True)
assert config['ascii'] is True
config['ascii'] = False
assert config['ascii'] is False
config['ascii'] = True
del config['ascii']
assert config['ascii'] is False
config.update(subreddit='cfb', new_value=2.0)
assert config['subreddit'] == 'cfb'
assert config['new_value'] == 2.0
def test_config_from_args():
"Ensure that command line arguments are parsed properly"
args = ['rtv',
'-s', 'cfb',
'-l', 'https://reddit.com/permalink •',
'--log', 'logfile.log',
'--ascii',
'--non-persistent',
'--clear-auth']
with mock.patch('sys.argv', ['rtv']):
config = Config()
config.from_args()
assert config.config == {}
with mock.patch('sys.argv', args):
config = Config()
config.from_args()
assert config['ascii'] is True
assert config['subreddit'] == 'cfb'
assert config['link'] == 'https://reddit.com/permalink •'
assert config['log'] == 'logfile.log'
assert config['ascii'] is True
assert config['persistent'] is False
assert config['clear_auth'] is True
def test_config_from_file():
"Ensure that config file arguments are parsed properly"
args = {
'ascii': True,
'persistent': False,
'clear_auth': True,
'log': 'logfile.log',
'link': 'https://reddit.com/permalink •',
'subreddit': 'cfb'}
with NamedTemporaryFile(suffix='.cfg') as fp:
config = Config(config_file=fp.name)
config.from_file()
assert config.config == {}
rows = ['{0}={1}'.format(key, val) for key, val in args.items()]
data = '\n'.join(['[rtv]'] + rows)
fp.write(codecs.encode(data, 'utf-8'))
fp.flush()
config.from_file()
assert config.config == args
def test_config_refresh_token():
"Ensure that the refresh token can be loaded, saved, and removed"
with NamedTemporaryFile(delete=False) as fp:
config = Config(token_file=fp.name)
# Write a new token to the file
config.refresh_token = 'secret_value'
config.save_refresh_token()
# Load a valid token from the file
config.refresh_token = None
config.load_refresh_token()
assert config.refresh_token == 'secret_value'
# Discard the token and delete the file
config.delete_refresh_token()
assert config.refresh_token is None
assert not os.path.exists(fp.name)
# Saving should create a new file
config.refresh_token = 'new_value'
config.save_refresh_token()
# Which we can read back to verify
config.refresh_token = None
config.load_refresh_token()
assert config.refresh_token == 'new_value'
# And delete again to clean up
config.delete_refresh_token()
assert not os.path.exists(fp.name)
# Loading from the non-existent file should return None
config.refresh_token = 'secret_value'
config.load_refresh_token()
assert config.refresh_token is None
def test_config_history():
"Ensure that the history can be loaded and saved"
with NamedTemporaryFile(delete=False) as fp:
config = Config(history_file=fp.name, history_size=3)
config.history.add('link1')
config.history.add('link2')
config.history.add('link3')
config.history.add('link4')
assert len(config.history) == 4
# Saving should only write the 3 most recent links
config.save_history()
config.load_history()
assert len(config.history) == 3
assert 'link1' not in config.history
assert 'link4' in config.history
config.delete_history()
assert len(config.history) == 0
assert not os.path.exists(fp.name)

287
tests/test_content.py Normal file
View File

@@ -0,0 +1,287 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
from itertools import islice
import six
import praw
import pytest
from rtv.content import (
Content, SubmissionContent, SubredditContent, SubscriptionContent)
from rtv import exceptions
try:
from unittest import mock
except ImportError:
import mock
def test_content_humanize_timestamp():
timestamp = time.time() - 30
assert Content.humanize_timestamp(timestamp) == '0min'
assert Content.humanize_timestamp(timestamp, True) == 'moments ago'
timestamp = time.time() - 60 * 60 * 24 * 30.4 * 12
assert Content.humanize_timestamp(timestamp) == '11month'
assert Content.humanize_timestamp(timestamp, True) == '11 months ago'
timestamp = time.time() - 60 * 60 * 24 * 30.4 * 12 * 5
assert Content.humanize_timestamp(timestamp) == '5yr'
assert Content.humanize_timestamp(timestamp, True) == '5 years ago'
def test_content_wrap_text():
text = 'four score\nand seven\n\n'
assert Content.wrap_text(text, 6) == ['four', 'score', 'and', 'seven', '']
assert Content.wrap_text(text, 15) == ['four score', 'and seven', '']
assert Content.wrap_text('', 70) == []
assert Content.wrap_text('\n\n\n\n', 70) == ['', '', '', '']
def test_content_submission_initialize(reddit, terminal):
url = 'https://www.reddit.com/r/Python/comments/2xmo63/'
submission = reddit.get_submission(url)
content = SubmissionContent(submission, terminal.loader, indent_size=3,
max_indent_level=4, order='top')
assert content.indent_size == 3
assert content.max_indent_level == 4
assert content.order == 'top'
assert content.name is not None
def test_content_submission(reddit, terminal):
url = 'https://www.reddit.com/r/Python/comments/2xmo63/'
submission = reddit.get_submission(url)
content = SubmissionContent(submission, terminal.loader)
# Everything is loaded upon instantiation
assert len(content._comment_data) == 45
assert content.get(-1)['type'] == 'Submission'
assert content.get(40)['type'] == 'Comment'
for data in content.iterate(-1, 1):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type'))
# All text should be converted to unicode by this point
for val in data.values():
assert not isinstance(val, six.binary_type)
# Out of bounds
with pytest.raises(IndexError):
content.get(-2)
with pytest.raises(IndexError):
content.get(50)
# Toggling the submission doesn't do anything
content.toggle(-1)
assert len(content._comment_data) == 45
# Toggling a comment hides its 3 children
content.toggle(2)
data = content.get(2)
assert data['type'] == 'HiddenComment'
assert data['count'] == 3
assert data['level'] >= content.get(3)['level']
assert len(content._comment_data) == 43
# Toggling again expands the children
content.toggle(2)
assert len(content._comment_data) == 45
def test_content_submission_load_more_comments(reddit, terminal):
url = 'https://www.reddit.com/r/AskReddit/comments/2np694/'
submission = reddit.get_submission(url)
content = SubmissionContent(submission, terminal.loader)
assert len(content._comment_data) == 391
# More comments load when toggled
assert content.get(390)['type'] == 'MoreComments'
content.toggle(390)
assert len(content._comment_data) > 390
assert content.get(390)['type'] == 'Comment'
def test_content_submission_from_url(reddit, terminal):
url = 'https://www.reddit.com/r/AskReddit/comments/2np694/'
SubmissionContent.from_url(reddit, url, terminal.loader)
SubmissionContent.from_url(reddit, url, terminal.loader, order='new')
# Invalid sorting order doesn't raise an exception
with terminal.loader():
SubmissionContent.from_url(reddit, url, terminal.loader, order='fake')
assert not terminal.loader.exception
# Invalid comment URL
with terminal.loader():
SubmissionContent.from_url(reddit, url[:-2], terminal.loader)
assert isinstance(terminal.loader.exception, praw.errors.NotFound)
def test_content_subreddit_initialize(reddit, terminal):
submissions = reddit.get_subreddit('python').get_top(limit=None)
content = SubredditContent('python', submissions, terminal.loader, 'top')
assert content.name == 'python'
assert content.order == 'top'
assert len(content._submission_data) == 1
def test_content_subreddit_initialize_invalid(reddit, terminal):
submissions = reddit.get_subreddit('invalidsubreddit7').get_top(limit=None)
with terminal.loader():
SubredditContent('python', submissions, terminal.loader, 'top')
assert isinstance(terminal.loader.exception, praw.errors.InvalidSubreddit)
def test_content_subreddit(reddit, terminal):
submissions = reddit.get_front_page(limit=5)
content = SubredditContent('front', submissions, terminal.loader)
# Submissions are loaded on demand, excluding for the first one
assert len(content._submission_data) == 1
assert content.get(0)['type'] == 'Submission'
for data in content.iterate(0, 1):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type',
'index', 'title', 'split_title'))
# All text should be converted to unicode by this point
for val in data.values():
assert not isinstance(val, six.binary_type)
# Out of bounds
with pytest.raises(IndexError):
content.get(-1)
with pytest.raises(IndexError):
content.get(5)
def test_content_subreddit_load_more(reddit, terminal):
submissions = reddit.get_front_page(limit=None)
content = SubredditContent('front', submissions, terminal.loader)
assert content.get(50)['type'] == 'Submission'
assert len(content._submission_data) == 51
for data in islice(content.iterate(0, 1, 70), 0, 50):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type',
'index', 'title', 'split_title'))
# All text should be converted to unicode by this point
for val in data.values():
assert not isinstance(val, six.binary_type)
def test_content_subreddit_from_name(reddit, terminal):
name = '/r/python'
content = SubredditContent.from_name(reddit, name, terminal.loader)
assert content.name == '/r/python'
assert content.order is None
# Can submit without the /r/ and with the order in the name
name = 'python/top/'
content = SubredditContent.from_name(reddit, name, terminal.loader)
assert content.name == '/r/python'
assert content.order == 'top'
# Explicit order trumps implicit
name = '/r/python/top'
content = SubredditContent.from_name(
reddit, name, terminal.loader, order='new')
assert content.name == '/r/python'
assert content.order == 'new'
# Invalid order raises an exception
name = '/r/python/fake'
with terminal.loader():
SubredditContent.from_name(reddit, name, terminal.loader)
assert isinstance(terminal.loader.exception, exceptions.SubredditError)
# Front page alias
name = '/r/front/rising'
content = SubredditContent.from_name(reddit, name, terminal.loader)
assert content.name == '/r/front'
assert content.order == 'rising'
# Queries
SubredditContent.from_name(reddit, 'front', terminal.loader, query='pea')
SubredditContent.from_name(reddit, 'python', terminal.loader, query='pea')
def test_content_subreddit_multireddit(reddit, terminal):
name = '/r/python+linux'
content = SubredditContent.from_name(reddit, name, terminal.loader)
assert content.name == '/r/python+linux'
# Invalid multireddit
name = '/r/a+b'
with terminal.loader():
SubredditContent.from_name(reddit, name, terminal.loader)
assert isinstance(terminal.loader.exception, praw.errors.NotFound)
def test_content_subreddit_me(reddit, oauth, refresh_token, terminal):
# Not logged in
with terminal.loader():
SubredditContent.from_name(reddit, '/r/me', terminal.loader)
assert isinstance(terminal.loader.exception, exceptions.AccountError)
# Logged in
oauth.config.refresh_token = refresh_token
oauth.authorize()
with terminal.loader():
SubredditContent.from_name(reddit, 'me', terminal.loader)
# If there is no submitted content, an error should be raised
if terminal.loader.exception:
assert isinstance(terminal.loader.exception, exceptions.SubredditError)
def test_content_subscription(reddit, oauth, refresh_token, terminal):
# Not logged in
with terminal.loader():
SubscriptionContent.from_user(reddit, terminal.loader)
assert isinstance(
terminal.loader.exception, praw.errors.LoginOrScopeRequired)
# Logged in
oauth.config.refresh_token = refresh_token
oauth.authorize()
with terminal.loader():
content = SubscriptionContent.from_user(reddit, terminal.loader)
assert terminal.loader.exception is None
# These are static
assert content.name == 'Subscriptions'
assert content.order is None
# Validate content
for data in content.iterate(0, 1, 70):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type',
'title', 'split_title'))
# All text should be converted to unicode by this point
for val in data.values():
assert not isinstance(val, six.binary_type)
def test_content_subscription_empty(terminal):
# Simulate an empty subscription generator
subscriptions = iter([])
with terminal.loader():
SubscriptionContent(subscriptions, terminal.loader)
assert isinstance(terminal.loader.exception, exceptions.SubscriptionError)

164
tests/test_oauth.py Normal file
View File

@@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
from tornado.web import Application
from tornado.testing import AsyncHTTPTestCase
from praw.errors import OAuthException
from rtv.oauth import OAuthHelper, OAuthHandler
from rtv.config import TEMPLATE
try:
from unittest import mock
except ImportError:
import mock
class TestAuthHandler(AsyncHTTPTestCase):
def get_app(self):
self.params = {}
handler = [('/', OAuthHandler, {'params': self.params})]
return Application(handler, template_path=TEMPLATE)
def test_no_callback(self):
resp = self.fetch('/')
assert resp.code == 200
assert self.params['error'] is None
assert 'Wait...' in resp.body.decode()
def test_access_denied(self):
resp = self.fetch('/?error=access_denied')
assert resp.code == 200
assert self.params['error'] == 'access_denied'
assert 'was denied access' in resp.body.decode()
def test_error(self):
resp = self.fetch('/?error=fake')
assert resp.code == 200
assert self.params['error'] == 'fake'
assert 'fake' in resp.body.decode()
def test_success(self):
resp = self.fetch('/?state=fake_state&code=fake_code')
assert resp.code == 200
assert self.params['error'] is None
assert self.params['state'] == 'fake_state'
assert self.params['code'] == 'fake_code'
assert 'Access Granted' in resp.body.decode()
def test_oauth_terminal_non_mobile_authorize(reddit, terminal, config):
# Should direct to the desktop version if using a graphical browser
reddit.config.API_PATHS['authorize'] = 'api/v1/authorize/'
terminal._display = True
oauth = OAuthHelper(reddit, terminal, config)
assert '.compact' not in oauth.reddit.config.API_PATHS['authorize']
def test_oauth_terminal_mobile_authorize(reddit, terminal, config):
# Should direct to the mobile version if using a terminal browser
reddit.config.API_PATHS['authorize'] = 'api/v1/authorize/'
terminal._display = False
oauth = OAuthHelper(reddit, terminal, config)
assert '.compact' in oauth.reddit.config.API_PATHS['authorize']
def test_oauth_authorize_with_refresh_token(oauth, stdscr, refresh_token):
oauth.config.refresh_token = refresh_token
oauth.authorize()
assert oauth.http_server is None
# We should be able to handle an oauth failure
with mock.patch.object(oauth.reddit, 'refresh_access_information'):
exception = OAuthException('', '')
oauth.reddit.refresh_access_information.side_effect = exception
oauth.authorize()
message = 'Invalid OAuth data'.encode('utf-8')
stdscr.derwin().addstr.assert_called_with(1, 1, message)
assert oauth.http_server is None
def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
# Because we use `from .helpers import open_browser` we have to patch the
# function in the destination oauth module and not the helpers module
with mock.patch('uuid.UUID.hex', new_callable=mock.PropertyMock) as uuid, \
mock.patch('rtv.terminal.Terminal.open_browser') as open_browser, \
mock.patch('rtv.oauth.ioloop') as ioloop, \
mock.patch('rtv.oauth.httpserver'), \
mock.patch.object(oauth.reddit, 'user'), \
mock.patch('time.sleep'):
io = ioloop.IOLoop.current.return_value
# Valid authorization
oauth.term._display = False
params = {'state': 'uniqueid', 'code': 'secretcode', 'error': None}
uuid.return_value = params['state']
io.start.side_effect = lambda *_: oauth.params.update(**params)
oauth.authorize()
assert not open_browser.called
oauth.reddit.get_access_information.assert_called_with(
reddit, params['code'])
assert oauth.config.refresh_token is not None
assert oauth.config.save_refresh_token.called
stdscr.reset_mock()
oauth.reddit.get_access_information.reset_mock()
oauth.config.save_refresh_token.reset_mock()
oauth.http_server = None
# The next authorization should skip the oauth process
oauth.config.refresh_token = refresh_token
oauth.authorize()
assert oauth.reddit.user is not None
assert oauth.http_server is None
stdscr.reset_mock()
# Invalid state returned
params = {'state': 'uniqueid', 'code': 'secretcode', 'error': None}
oauth.config.refresh_token = None
uuid.return_value = 'invalidcode'
oauth.authorize()
error_message = 'UUID mismatch'.encode('utf-8')
stdscr.subwin.addstr.assert_any_call(1, 1, error_message)
# Valid authorization, terminal browser
oauth.term._display = True
params = {'state': 'uniqueid', 'code': 'secretcode', 'error': None}
uuid.return_value = params['state']
io.start.side_effect = lambda *_: oauth.params.update(**params)
oauth.authorize()
assert open_browser.called
oauth.reddit.get_access_information.assert_called_with(
reddit, params['code'])
assert oauth.config.refresh_token is not None
assert oauth.config.save_refresh_token.called
stdscr.reset_mock()
oauth.reddit.get_access_information.reset_mock()
oauth.config.refresh_token = None
oauth.config.save_refresh_token.reset_mock()
oauth.http_server = None
# Exceptions when logging in are handled correctly
with mock.patch.object(oauth.reddit, 'get_access_information'):
exception = OAuthException('', '')
oauth.reddit.get_access_information.side_effect = exception
oauth.authorize()
message = 'Invalid OAuth data'.encode('utf-8')
stdscr.derwin().addstr.assert_called_with(1, 1, message)
assert not oauth.config.save_refresh_token.called
def test_oauth_clear_data(oauth):
oauth.config.refresh_token = 'secrettoken'
oauth.reddit.refresh_token = 'secrettoken'
oauth.clear_oauth_data()
assert oauth.config.refresh_token is None
assert oauth.reddit.refresh_token is None

443
tests/test_objects.py Normal file
View File

@@ -0,0 +1,443 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import curses
import pytest
import requests
from rtv.objects import Color, Controller, Navigator, curses_session
try:
from unittest import mock
except ImportError:
import mock
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen(terminal, stdscr, ascii):
terminal.ascii = ascii
# Ensure the thread is properly started/stopped
with terminal.loader(delay=0, message=u'Hello', trail=u'...'):
assert terminal.loader._animator.is_alive()
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert terminal.loader.exception is None
assert stdscr.subwin.ncols == 10
assert stdscr.subwin.nlines == 3
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_exception_unhandled(terminal, stdscr, ascii):
terminal.ascii = ascii
# Raising an exception should clean up the loader properly
with pytest.raises(Exception):
with terminal.loader(delay=0):
assert terminal.loader._animator.is_alive()
raise Exception()
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_exception_handled(terminal, stdscr, ascii):
terminal.ascii = ascii
# Raising a handled exception should get stored on the loaders
with terminal.loader(delay=0):
assert terminal.loader._animator.is_alive()
raise requests.ConnectionError()
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert isinstance(terminal.loader.exception, requests.ConnectionError)
error_message = 'Connection Error'.encode('ascii' if ascii else 'utf-8')
stdscr.subwin.addstr.assert_called_with(1, 1, error_message)
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_exception_not_caught(terminal, stdscr, ascii):
terminal.ascii = ascii
with pytest.raises(KeyboardInterrupt):
with terminal.loader(delay=0, catch_exception=False):
assert terminal.loader._animator.is_alive()
raise KeyboardInterrupt()
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert terminal.loader.exception is None
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_keyboard_interrupt(terminal, stdscr, ascii):
terminal.ascii = ascii
# Raising a KeyboardInterrupt should be also be stored
with terminal.loader(delay=0):
assert terminal.loader._animator.is_alive()
raise KeyboardInterrupt()
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert isinstance(terminal.loader.exception, KeyboardInterrupt)
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_escape(terminal, stdscr, ascii):
terminal.ascii = ascii
stdscr.getch.return_value = terminal.ESCAPE
# Pressing escape should trigger an interrupt during the delay section
with mock.patch('os.kill') as kill:
with terminal.loader():
time.sleep(0.1)
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert kill.called
# As will as during the animation section
with mock.patch('os.kill') as kill:
with terminal.loader(delay=0):
time.sleep(0.1)
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
assert kill.called
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_initial_delay(terminal, stdscr, ascii):
terminal.ascii = ascii
# If we don't reach the initial delay nothing should be drawn
with terminal.loader(delay=0.1):
time.sleep(0.05)
assert not stdscr.subwin.addstr.called
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_nested(terminal, ascii):
terminal.ascii = ascii
with terminal.loader(message='Outer'):
with terminal.loader(message='Inner'):
raise requests.ConnectionError()
assert False # Should never be reached
assert isinstance(terminal.loader.exception, requests.ConnectionError)
assert terminal.loader.depth == 0
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
@pytest.mark.parametrize('ascii', [True, False])
def test_objects_load_screen_nested_complex(terminal, stdscr, ascii):
terminal.ascii = ascii
with terminal.loader(message='Outer') as outer_loader:
assert outer_loader.depth == 1
with terminal.loader(message='Inner') as inner_loader:
assert inner_loader.depth == 2
assert inner_loader._args[2] == 'Outer'
with terminal.loader():
assert terminal.loader.depth == 2
raise requests.ConnectionError()
assert False # Should never be reached
assert isinstance(terminal.loader.exception, requests.ConnectionError)
assert terminal.loader.depth == 0
assert not terminal.loader._is_running
assert not terminal.loader._animator.is_alive()
error_message = 'Connection Error'.encode('ascii' if ascii else 'utf-8')
stdscr.subwin.addstr.assert_called_once_with(1, 1, error_message)
def test_objects_color(stdscr):
colors = ['RED', 'GREEN', 'YELLOW', 'BLUE', 'MAGENTA', 'CYAN', 'WHITE']
# Check that all colors start with the default value
for color in colors:
assert getattr(Color, color) == curses.A_NORMAL
Color.init()
assert curses.use_default_colors.called
# Check that all colors are populated
for color in colors:
assert getattr(Color, color) == 23
def test_objects_curses_session(stdscr):
# Normal setup and cleanup
with curses_session():
pass
assert curses.initscr.called
assert curses.endwin.called
curses.initscr.reset_mock()
curses.endwin.reset_mock()
# Ensure cleanup runs if an error occurs
with pytest.raises(KeyboardInterrupt):
with curses_session():
raise KeyboardInterrupt()
assert curses.initscr.called
assert curses.endwin.called
curses.initscr.reset_mock()
curses.endwin.reset_mock()
# But cleanup shouldn't run if stdscr was never instantiated
curses.initscr.side_effect = KeyboardInterrupt
with pytest.raises(KeyboardInterrupt):
with curses_session():
pass
assert curses.initscr.called
assert not curses.endwin.called
curses.initscr.reset_mock()
curses.endwin.reset_mock()
def test_objects_controller():
class ControllerA(Controller):
character_map = {}
class ControllerB(ControllerA):
character_map = {}
class ControllerC(ControllerA):
character_map = {}
@ControllerA.register('1')
def call_page(_):
return 'a1'
@ControllerA.register('2')
def call_page(_):
return 'a2'
@ControllerB.register('1')
def call_page(_):
return 'b1'
@ControllerC.register('2')
def call_page(_):
return 'c2'
controller_a = ControllerA(None)
controller_b = ControllerB(None)
controller_c = ControllerC(None)
assert controller_a.trigger('1') == 'a1'
assert controller_a.trigger('2') == 'a2'
assert controller_a.trigger('3') is None
assert controller_b.trigger('1') == 'b1'
assert controller_b.trigger('2') == 'a2'
assert controller_b.trigger('3') is None
assert controller_c.trigger('1') == 'a1'
assert controller_c.trigger('2') == 'c2'
assert controller_c.trigger('3') is None
def test_objects_navigator_properties():
def valid_page_cb(_):
return
nav = Navigator(valid_page_cb)
assert nav.step == 1
assert nav.position == (0, 0, False)
assert nav.absolute_index == 0
nav = Navigator(valid_page_cb, 5, 2, True)
assert nav.step == -1
assert nav.position == (5, 2, True)
assert nav.absolute_index == 3
def test_objects_navigator_move():
def valid_page_cb(index):
if index < 0 or index > 3:
raise IndexError()
nav = Navigator(valid_page_cb)
# Try to scroll up past the first item
valid, redraw = nav.move(-1, 2)
assert not valid
assert not redraw
# Scroll down
valid, redraw = nav.move(1, 3)
assert nav.page_index == 0
assert nav.cursor_index == 1
assert valid
assert not redraw
# Scroll down, reach last item on the page and flip the screen
valid, redraw = nav.move(1, 3)
assert nav.page_index == 2
assert nav.cursor_index == 0
assert nav.inverted
assert valid
assert redraw
# Keep scrolling
valid, redraw = nav.move(1, 3)
assert nav.page_index == 3
assert nav.cursor_index == 0
assert nav.inverted
assert valid
assert redraw
# Reach the end of the page and stop
valid, redraw = nav.move(1, 1)
assert nav.page_index == 3
assert nav.cursor_index == 0
assert nav.inverted
assert not valid
assert not redraw
# Last item was large and takes up the whole screen, scroll back up and
# flip the screen again
valid, redraw = nav.move(-1, 1)
assert nav.page_index == 2
assert nav.cursor_index == 0
assert not nav.inverted
assert valid
assert redraw
def test_objects_navigator_move_new_submission():
def valid_page_cb(index):
if index != -1:
raise IndexError()
nav = Navigator(valid_page_cb, page_index=-1)
# Can't move up
valid, redraw = nav.move(-1, 1)
assert nav.page_index == -1
assert nav.cursor_index == 0
assert not nav.inverted
assert not valid
assert not redraw
# Can't move down
valid, redraw = nav.move(1, 1)
assert nav.page_index == -1
assert nav.cursor_index == 0
assert not nav.inverted
assert not valid
assert not redraw
def test_objects_navigator_move_submission():
def valid_page_cb(index):
if index < -1 or index > 4:
raise IndexError()
nav = Navigator(valid_page_cb, page_index=-1)
# Can't move up
valid, redraw = nav.move(-1, 2)
assert nav.page_index == -1
assert nav.cursor_index == 0
assert not nav.inverted
assert not valid
assert not redraw
# Moving down jumps to the first comment
valid, redraw = nav.move(1, 2)
assert nav.page_index == 0
assert nav.cursor_index == 0
assert not nav.inverted
assert valid
assert redraw
# Moving down again inverts the screen
valid, redraw = nav.move(1, 2)
assert nav.page_index == 1
assert nav.cursor_index == 0
assert nav.inverted
assert valid
assert redraw
# Move up to the first comment
valid, redraw = nav.move(-1, 2)
assert nav.page_index == 0
assert nav.cursor_index == 0
assert not nav.inverted
assert valid
assert redraw
# Move up to the submission
valid, redraw = nav.move(-1, 2)
assert nav.page_index == -1
assert nav.cursor_index == 0
assert not nav.inverted
assert valid
assert redraw
@pytest.mark.xfail(reason="Paging is still broken in several edge-cases")
def test_objects_navigator_move_page():
def valid_page_cb(index):
if index < 0 or index > 7:
raise IndexError()
nav = Navigator(valid_page_cb, cursor_index=2)
# Can't move up
valid, redraw = nav.move_page(-1, 5)
assert nav.page_index == 0
assert nav.cursor_index == 0
assert not nav.inverted
assert not valid
assert not redraw
# Page down
valid, redraw = nav.move_page(1, 5)
assert nav.page_index == 4
assert nav.cursor_index == 0
assert nav.inverted
assert valid
assert redraw
# Page up
valid, redraw = nav.move_page(-1, 3)
assert nav.page_index == 2
assert nav.cursor_index == 0
assert not nav.inverted
assert valid
assert redraw
def test_objects_navigator_flip():
def valid_page_cb(index):
if index < 0 or index > 10:
raise IndexError()
nav = Navigator(valid_page_cb)
nav.flip(5)
assert nav.page_index == 5
assert nav.cursor_index == 5
assert nav.inverted
nav.flip(3)
assert nav.page_index == 2
assert nav.cursor_index == 3
assert not nav.inverted

126
tests/test_page.py Normal file
View File

@@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytest
from rtv.page import Page, PageController, logged_in
try:
from unittest import mock
except ImportError:
import mock
def test_page_logged_in(terminal):
page = mock.MagicMock()
page.term = terminal
@logged_in
def func(page):
raise RuntimeError()
# Logged in runs the function
page.reddit.is_oauth_session.return_value = True
with pytest.raises(RuntimeError):
func(page)
message = 'Not logged in'.encode('utf-8')
with pytest.raises(AssertionError):
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, message)
# Logged out skips the function and displays a message
page.reddit.is_oauth_session.return_value = False
func(page)
message = 'Not logged in'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, message)
def test_page_unauthenticated(reddit, terminal, config, oauth):
page = Page(reddit, terminal, config, oauth)
page.controller = PageController(page)
with mock.patch.object(page, 'refresh_content'), \
mock.patch.object(page, 'content'), \
mock.patch.object(page, 'nav'), \
mock.patch.object(page, 'draw'):
# Loop
def func(ch):
page.active = False
with mock.patch.object(page, 'controller'):
page.controller.trigger = mock.MagicMock(side_effect=func)
page.loop()
assert page.draw.called
# Quit, confirm
terminal.stdscr.getch.return_value = ord('y')
with mock.patch('sys.exit') as sys_exit:
page.controller.trigger('q')
assert sys_exit.called
# Quit, deny
terminal.stdscr.getch.return_value = terminal.ESCAPE
with mock.patch('sys.exit') as sys_exit:
page.controller.trigger('q')
assert not sys_exit.called
# Force quit
terminal.stdscr.getch.return_value = terminal.ESCAPE
with mock.patch('sys.exit') as sys_exit:
page.controller.trigger('Q')
assert sys_exit.called
# Show help
page.controller.trigger('?')
message = 'Basic Commands'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_any_call(1, 1, message)
# Sort content
page.controller.trigger('1')
page.refresh_content.assert_called_with(order='hot')
page.controller.trigger('2')
page.refresh_content.assert_called_with(order='top')
page.controller.trigger('3')
page.refresh_content.assert_called_with(order='rising')
page.controller.trigger('4')
page.refresh_content.assert_called_with(order='new')
page.controller.trigger('5')
page.refresh_content.assert_called_with(order='controversial')
logged_in_methods = [
'a', # Upvote
'z', # Downvote
'd', # Delete
'e', # Edit
'i', # Get inbox
]
for ch in logged_in_methods:
page.controller.trigger(ch)
message = 'Not logged in'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, message)
terminal.stdscr.subwin.addstr.reset_mock()
def test_page_authenticated(reddit, terminal, config, oauth, refresh_token):
page = Page(reddit, terminal, config, oauth)
page.controller = PageController(page)
config.refresh_token = refresh_token
# Login
page.controller.trigger('u')
assert reddit.is_oauth_session()
# Get inbox - Call the real method
page.controller.trigger('i')
# Get inbox - Simulate no new messages
reddit.get_unread = mock.Mock(return_value=[])
page.controller.trigger('i')
message = 'No New Messages'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, message)
# Logout
terminal.stdscr.getch.return_value = ord('y')
page.controller.trigger('u')
assert not reddit.is_oauth_session()

213
tests/test_submission.py Normal file
View File

@@ -0,0 +1,213 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import curses
from rtv.submission import SubmissionPage
try:
from unittest import mock
except ImportError:
import mock
def test_submission_page_construct(reddit, terminal, config, oauth):
window = terminal.stdscr.subwin
url = ('https://www.reddit.com/r/Python/comments/2xmo63/'
'a_python_terminal_viewer_for_browsing_reddit')
with terminal.loader():
page = SubmissionPage(reddit, terminal, config, oauth, url=url)
assert terminal.loader.exception is None
# Toggle the second comment so we can check the draw more comments method
page.content.toggle(1)
page.draw()
# Title
title = url[:terminal.stdscr.ncols-1].encode('utf-8')
window.addstr.assert_any_call(0, 0, title)
# Submission
submission_data = page.content.get(-1)
text = submission_data['title'].encode('utf-8')
window.subwin.addstr.assert_any_call(1, 1, text, 2097152)
assert window.subwin.border.called
# Comment
comment_data = page.content.get(0)
text = comment_data['split_body'][0].encode('utf-8')
window.subwin.addstr.assert_any_call(1, 1, text)
# More Comments
comment_data = page.content.get(1)
text = comment_data['body'].encode('utf-8')
window.subwin.addstr.assert_any_call(0, 1, text)
# Cursor should not be drawn when the page is first opened
assert not window.subwin.chgat.called
# Reload with a smaller terminal window
terminal.stdscr.ncols = 20
terminal.stdscr.nlines = 10
with terminal.loader():
page = SubmissionPage(reddit, terminal, config, oauth, url=url)
assert terminal.loader.exception is None
page.draw()
def test_submission_refresh(submission_page):
# Should be able to refresh content
submission_page.refresh_content()
def test_submission_unauthenticated(submission_page, terminal):
# Unauthenticated commands
methods = [
'a', # Upvote
'z', # Downvote
'c', # Comment
'e', # Edit
'd', # Delete
]
for ch in methods:
submission_page.controller.trigger(ch)
text = 'Not logged in'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, text)
def test_submission_open(submission_page, terminal):
# Open the selected link with the web browser
with mock.patch.object(terminal, 'open_browser'):
submission_page.controller.trigger(terminal.RETURN)
assert terminal.open_browser.called
def test_submission_vote(submission_page, refresh_token):
# Log in
submission_page.config.refresh_token = refresh_token
submission_page.oauth.authorize()
# Test voting on the submission
with mock.patch('praw.objects.Submission.upvote') as upvote, \
mock.patch('praw.objects.Submission.downvote') as downvote, \
mock.patch('praw.objects.Submission.clear_vote') as clear_vote:
data = submission_page.content.get(submission_page.nav.absolute_index)
# Upvote
submission_page.controller.trigger('a')
assert upvote.called
assert data['likes'] is True
# Downvote
submission_page.controller.trigger('z')
assert downvote.called
assert data['likes'] is False
# Clear vote
submission_page.controller.trigger('z')
assert clear_vote.called
assert data['likes'] is None
# Upvote - exception
upvote.side_effect = KeyboardInterrupt
submission_page.controller.trigger('a')
assert data['likes'] is None
# Downvote - exception
downvote.side_effect = KeyboardInterrupt
submission_page.controller.trigger('a')
assert data['likes'] is None
def test_submission_comment(submission_page, terminal, refresh_token):
# Log in
submission_page.config.refresh_token = refresh_token
submission_page.oauth.authorize()
# Leave a comment
with mock.patch('praw.objects.Submission.add_comment') as add_comment, \
mock.patch.object(terminal, 'open_editor') as open_editor, \
mock.patch('time.sleep'):
open_editor.return_value = 'comment text'
submission_page.controller.trigger('c')
assert open_editor.called
add_comment.assert_called_with('comment text')
def test_submission_delete(submission_page, terminal, refresh_token):
# Log in
submission_page.config.refresh_token = refresh_token
submission_page.oauth.authorize()
# Can't delete the submission
curses.flash.reset_mock()
submission_page.controller.trigger('d')
assert curses.flash.called
# Move down to the first comment
with mock.patch.object(submission_page, 'clear_input_queue'):
submission_page.controller.trigger('j')
# Try to delete the first comment - wrong author
curses.flash.reset_mock()
submission_page.controller.trigger('d')
assert curses.flash.called
# Spoof the author and try to delete again
data = submission_page.content.get(submission_page.nav.absolute_index)
data['author'] = submission_page.reddit.user.name
with mock.patch('praw.objects.Comment.delete') as delete, \
mock.patch.object(terminal.stdscr, 'getch') as getch, \
mock.patch('time.sleep'):
getch.return_value = ord('y')
submission_page.controller.trigger('d')
assert delete.called
def test_submission_edit(submission_page, terminal, refresh_token):
# Log in
submission_page.config.refresh_token = refresh_token
submission_page.oauth.authorize()
# Try to edit the submission - wrong author
curses.flash.reset_mock()
submission_page.controller.trigger('e')
assert curses.flash.called
# Spoof the submission and try to edit again
data = submission_page.content.get(submission_page.nav.absolute_index)
data['author'] = submission_page.reddit.user.name
with mock.patch('praw.objects.Submission.edit') as edit, \
mock.patch.object(terminal, 'open_editor') as open_editor, \
mock.patch('time.sleep'):
open_editor.return_value = 'submission text'
submission_page.controller.trigger('e')
assert open_editor.called
edit.assert_called_with('submission text')
# Move down to the first comment
with mock.patch.object(submission_page, 'clear_input_queue'):
submission_page.controller.trigger('j')
# Spoof the author and edit the comment
data = submission_page.content.get(submission_page.nav.absolute_index)
data['author'] = submission_page.reddit.user.name
with mock.patch('praw.objects.Comment.edit') as edit, \
mock.patch.object(terminal, 'open_editor') as open_editor, \
mock.patch('time.sleep'):
open_editor.return_value = 'comment text'
submission_page.controller.trigger('e')
assert open_editor.called
edit.assert_called_with('comment text')

172
tests/test_subreddit.py Normal file
View File

@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from rtv.subreddit import SubredditPage
from rtv.content import SubmissionContent
try:
from unittest import mock
except ImportError:
import mock
def test_subreddit_page_construct(reddit, terminal, config, oauth):
window = terminal.stdscr.subwin
with terminal.loader():
page = SubredditPage(reddit, terminal, config, oauth, '/r/python')
assert terminal.loader.exception is None
page.draw()
# Title
title = '/r/python'.encode('utf-8')
window.addstr.assert_any_call(0, 0, title)
# Submission
text = page.content.get(0)['split_title'][0].encode('utf-8')
window.subwin.addstr.assert_any_call(0, 1, text, 2097152)
# Cursor should have been drawn
assert window.subwin.chgat.called
# Reload with a smaller terminal window
terminal.stdscr.ncols = 20
terminal.stdscr.nlines = 10
with terminal.loader():
page = SubredditPage(reddit, terminal, config, oauth, '/r/python')
assert terminal.loader.exception is None
page.draw()
def test_subreddit_refresh(subreddit_page, terminal):
# Refresh the page with default values
subreddit_page.controller.trigger('r')
assert subreddit_page.content.order is None
assert subreddit_page.content.name == '/r/python'
assert terminal.loader.exception is None
# Refresh with the order in the name
subreddit_page.refresh_content(name='/r/front/hot', order='ignore')
assert subreddit_page.content.order == 'hot'
assert subreddit_page.content.name == '/r/front'
assert terminal.loader.exception is None
def test_subreddit_search(subreddit_page, terminal):
# Search the current subreddit
with mock.patch.object(terminal, 'prompt_input'):
terminal.prompt_input.return_value = 'search term'
subreddit_page.controller.trigger('f')
assert subreddit_page.content.name == '/r/python'
assert terminal.prompt_input.called
assert not terminal.loader.exception
# Searching with an empty query shouldn't crash
with mock.patch.object(terminal, 'prompt_input'):
terminal.prompt_input.return_value = None
subreddit_page.controller.trigger('f')
assert not terminal.loader.exception
def test_subreddit_prompt(subreddit_page, terminal):
# Prompt for a different subreddit
with mock.patch.object(terminal, 'prompt_input'):
terminal.prompt_input.return_value = 'front/top'
subreddit_page.controller.trigger('/')
assert subreddit_page.content.name == '/r/front'
assert subreddit_page.content.order == 'top'
assert not terminal.loader.exception
def test_subreddit_open(subreddit_page, terminal, config):
# Open the selected submission
data = subreddit_page.content.get(subreddit_page.nav.absolute_index)
with mock.patch('rtv.submission.SubmissionPage.loop') as loop, \
mock.patch.object(config.history, 'add'):
data['url_type'] = 'selfpost'
subreddit_page.controller.trigger('l')
assert not terminal.loader.exception
assert loop.called
config.history.add.assert_called_with(data['url_full'])
# Open the selected link externally
with mock.patch.object(terminal, 'open_browser'), \
mock.patch.object(config.history, 'add'):
data['url_type'] = 'external'
subreddit_page.controller.trigger('o')
assert terminal.open_browser.called
config.history.add.assert_called_with(data['url_full'])
# Open the selected link within rtv
with mock.patch.object(subreddit_page, 'open_submission'), \
mock.patch.object(config.history, 'add'):
data['url_type'] = 'selfpost'
subreddit_page.controller.trigger('o')
assert subreddit_page.open_submission.called
def test_subreddit_unauthenticated(subreddit_page, terminal):
# Unauthenticated commands
methods = [
'a', # Upvote
'z', # Downvote
'c', # Post
'e', # Edit
'd', # Delete
's', # Subscriptions
]
for ch in methods:
subreddit_page.controller.trigger(ch)
text = 'Not logged in'.encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, text)
def test_subreddit_post(subreddit_page, terminal, reddit, refresh_token):
# Log in
subreddit_page.config.refresh_token = refresh_token
subreddit_page.oauth.authorize()
# Post a submission to an invalid subreddit
subreddit_page.refresh_content('front')
subreddit_page.controller.trigger('c')
text = "Can't post to /r/front".encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, text)
# Post a submission with a title but with no body
subreddit_page.refresh_content('python')
with mock.patch.object(terminal, 'open_editor'):
terminal.open_editor.return_value = 'title'
subreddit_page.controller.trigger('c')
text = "Aborted".encode('utf-8')
terminal.stdscr.subwin.addstr.assert_called_with(1, 1, text)
# Post a fake submission
url = 'https://www.reddit.com/r/Python/comments/2xmo63/'
submission = reddit.get_submission(url)
with mock.patch.object(terminal, 'open_editor'), \
mock.patch.object(reddit, 'submit'), \
mock.patch('rtv.page.Page.loop') as loop, \
mock.patch('time.sleep'):
terminal.open_editor.return_value = 'test\ncontent'
reddit.submit.return_value = submission
subreddit_page.controller.trigger('c')
assert reddit.submit.called
assert loop.called
def test_subreddit_open_subscriptions(subreddit_page, refresh_token):
# Log in
subreddit_page.config.refresh_token = refresh_token
subreddit_page.oauth.authorize()
# Open a subscription
with mock.patch('rtv.page.Page.loop') as loop:
subreddit_page.controller.trigger('s')
assert loop.called

135
tests/test_subscription.py Normal file
View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import curses
import praw
from rtv.subscription import SubscriptionPage
try:
from unittest import mock
except ImportError:
import mock
def test_subscription_page_construct(reddit, terminal, config, oauth,
refresh_token):
window = terminal.stdscr.subwin
# Can't load the page if not logged in
with terminal.loader():
SubscriptionPage(reddit, terminal, config, oauth)
assert isinstance(
terminal.loader.exception, praw.errors.LoginOrScopeRequired)
# Log in
config.refresh_token = refresh_token
oauth.authorize()
with terminal.loader():
page = SubscriptionPage(reddit, terminal, config, oauth)
assert terminal.loader.exception is None
page.draw()
# Header - Title
title = 'Subscriptions'.encode('utf-8')
window.addstr.assert_any_call(0, 0, title)
# Header - Name
name = reddit.user.name.encode('utf-8')
window.addstr.assert_any_call(0, 59, name)
# Cursor - 2 lines
window.subwin.chgat.assert_any_call(0, 0, 1, 262144)
window.subwin.chgat.assert_any_call(1, 0, 1, 262144)
# Reload with a smaller terminal window
terminal.stdscr.ncols = 20
terminal.stdscr.nlines = 10
with terminal.loader():
page = SubscriptionPage(reddit, terminal, config, oauth)
assert terminal.loader.exception is None
page.draw()
def test_subscription_refresh(subscription_page):
# Refresh content - invalid order
subscription_page.controller.trigger('2')
assert curses.flash.called
curses.flash.reset_mock()
# Refresh content
subscription_page.controller.trigger('r')
assert not curses.flash.called
def test_subscription_move(subscription_page):
# Test movement
with mock.patch.object(subscription_page, 'clear_input_queue'):
# Move cursor to the bottom of the page
while not curses.flash.called:
subscription_page.controller.trigger('j')
curses.flash.reset_mock()
assert subscription_page.nav.inverted
assert (subscription_page.nav.absolute_index ==
len(subscription_page.content._subscription_data) - 1)
# And back to the top
for i in range(subscription_page.nav.absolute_index):
subscription_page.controller.trigger('k')
assert not curses.flash.called
assert subscription_page.nav.absolute_index == 0
assert not subscription_page.nav.inverted
# Can't go up any further
subscription_page.controller.trigger('k')
assert curses.flash.called
assert subscription_page.nav.absolute_index == 0
assert not subscription_page.nav.inverted
# Page down should move the last item to the top
n = len(subscription_page._subwindows)
subscription_page.controller.trigger('n')
assert subscription_page.nav.absolute_index == n - 1
# And page up should move back up, but possibly not to the first item
subscription_page.controller.trigger('m')
def test_subscription_select(subscription_page):
# Select a subreddit
subscription_page.controller.trigger(curses.KEY_ENTER)
assert subscription_page.subreddit_data is not None
assert subscription_page.active is False
def test_subscription_close(subscription_page):
# Close the subscriptions page
subscription_page.subreddit_data = None
subscription_page.active = None
subscription_page.controller.trigger('h')
assert subscription_page.subreddit_data is None
assert subscription_page.active is False
def test_subscription_page_invalid(subscription_page):
# Test that other commands don't crash
methods = [
'a', # Upvote
'z', # Downvote
'd', # Delete
'e', # Edit
]
for ch in methods:
curses.flash.reset_mock()
subscription_page.controller.trigger(ch)
assert curses.flash.called

293
tests/test_terminal.py Normal file
View File

@@ -0,0 +1,293 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import curses
import codecs
import six
import pytest
from rtv.docs import HELP, COMMENT_EDIT_FILE
from rtv.objects import Color
try:
from unittest import mock
except ImportError:
import mock
def test_terminal_properties(terminal, config):
assert len(terminal.up_arrow) == 2
assert isinstance(terminal.up_arrow[0], six.text_type)
assert len(terminal.down_arrow) == 2
assert isinstance(terminal.down_arrow[0], six.text_type)
assert len(terminal.neutral_arrow) == 2
assert isinstance(terminal.neutral_arrow[0], six.text_type)
assert len(terminal.guilded) == 2
assert isinstance(terminal.guilded[0], six.text_type)
terminal._display = None
with mock.patch.dict('os.environ', {'DISPLAY': ''}):
assert terminal.display is False
terminal._display = None
with mock.patch.dict('os.environ', {'DISPLAY': ':0', 'BROWSER': 'w3m'}):
assert terminal.display is False
terminal._display = None
with mock.patch.dict('os.environ', {'DISPLAY': ':0', 'BROWSER': ''}), \
mock.patch('webbrowser._tryorder'):
assert terminal.display is True
assert terminal.get_arrow(None) is not None
assert terminal.get_arrow(True) is not None
assert terminal.get_arrow(False) is not None
assert terminal.ascii == config['ascii']
assert terminal.loader is not None
assert terminal.MIN_HEIGHT is not None
assert terminal.MIN_WIDTH is not None
def test_terminal_functions(terminal):
terminal.flash()
assert curses.flash.called
terminal.getch()
assert terminal.stdscr.getch.called
with pytest.raises(RuntimeError):
with terminal.no_delay():
raise RuntimeError()
terminal.stdscr.nodelay.assert_any_call(0)
terminal.stdscr.nodelay.assert_any_call(1)
curses.endwin.reset_mock()
curses.doupdate.reset_mock()
with terminal.suspend():
pass
assert curses.endwin.called
assert curses.doupdate.called
curses.endwin.reset_mock()
curses.doupdate.reset_mock()
with pytest.raises(RuntimeError):
with terminal.suspend():
raise RuntimeError()
assert curses.endwin.called
assert curses.doupdate.called
terminal.addch(terminal.stdscr, 3, 5, 'ch', 'attr')
terminal.stdscr.addch.assert_called_with(3, 5, 'ch', 'attr')
def test_terminal_clean_ascii(terminal):
terminal.ascii = True
# unicode returns ascii
text = terminal.clean('hello ❤')
assert isinstance(text, six.binary_type)
assert text.decode('ascii') == 'hello ?'
# utf-8 returns ascii
text = terminal.clean('hello ❤'.encode('utf-8'))
assert isinstance(text, six.binary_type)
assert text.decode('ascii') == 'hello ?'
# ascii returns ascii
text = terminal.clean('hello'.encode('ascii'))
assert isinstance(text, six.binary_type)
assert text.decode('ascii') == 'hello'
def test_terminal_clean_unicode(terminal):
terminal.ascii = False
# unicode returns utf-8
text = terminal.clean('hello ❤')
assert isinstance(text, six.binary_type)
assert text.decode('utf-8') == 'hello ❤'
# utf-8 returns utf-8
text = terminal.clean('hello ❤'.encode('utf-8'))
assert isinstance(text, six.binary_type)
assert text.decode('utf-8') == 'hello ❤'
# ascii returns utf-8
text = terminal.clean('hello'.encode('ascii'))
assert isinstance(text, six.binary_type)
assert text.decode('utf-8') == 'hello'
def test_terminal_clean_ncols(terminal):
text = terminal.clean('hello', n_cols=5)
assert text.decode('utf-8') == 'hello'
text = terminal.clean('hello', n_cols=4)
assert text.decode('utf-8') == 'hell'
text = terminal.clean('', n_cols=10)
assert text.decode('utf-8') == ''
text = terminal.clean('', n_cols=9)
assert text.decode('utf-8') == ''
@pytest.mark.parametrize('ascii', [True, False])
def test_terminal_add_line(terminal, stdscr, ascii):
terminal.ascii = ascii
terminal.add_line(stdscr, 'hello')
assert stdscr.addstr.called_with(0, 0, 'hello'.encode('ascii'))
stdscr.reset_mock()
# Text will be drawn, but cut off to fit on the screen
terminal.add_line(stdscr, 'hello', row=3, col=75)
assert stdscr.addstr.called_with((3, 75, 'hell'.encode('ascii')))
stdscr.reset_mock()
# Outside of screen bounds, don't even try to draw the text
terminal.add_line(stdscr, 'hello', col=79)
assert not stdscr.addstr.called
stdscr.reset_mock()
@pytest.mark.parametrize('ascii', [True, False])
def test_show_notification(terminal, stdscr, ascii):
terminal.ascii = ascii
# The whole message should fit in 40x80
text = HELP.strip().splitlines()
terminal.show_notification(text)
assert stdscr.subwin.nlines == len(text) + 2
assert stdscr.subwin.ncols == 80
assert stdscr.subwin.addstr.call_count == len(text)
stdscr.reset_mock()
# The text should be trimmed to fit in 20x20
stdscr.nlines, stdscr.ncols = 15, 20
text = HELP.strip().splitlines()
terminal.show_notification(text)
assert stdscr.subwin.nlines == 15
assert stdscr.subwin.ncols == 20
assert stdscr.subwin.addstr.call_count == 13
@pytest.mark.parametrize('ascii', [True, False])
def test_text_input(terminal, stdscr, ascii):
terminal.ascii = ascii
stdscr.nlines = 1
# Text will be wrong because stdscr.inch() is not implemented
# But we can at least tell if text was captured or not
stdscr.getch.side_effect = [ord('h'), ord('i'), ord('!'), terminal.RETURN]
assert isinstance(terminal.text_input(stdscr), six.text_type)
stdscr.getch.side_effect = [ord('b'), ord('y'), ord('e'), terminal.ESCAPE]
assert terminal.text_input(stdscr) is None
stdscr.getch.side_effect = [ord('h'), curses.KEY_RESIZE, terminal.RETURN]
assert terminal.text_input(stdscr, allow_resize=True) is not None
stdscr.getch.side_effect = [ord('h'), curses.KEY_RESIZE, terminal.RETURN]
assert terminal.text_input(stdscr, allow_resize=False) is None
@pytest.mark.parametrize('ascii', [True, False])
def test_prompt_input(terminal, stdscr, ascii):
terminal.ascii = ascii
window = stdscr.derwin()
window.getch.side_effect = [ord('h'), ord('i'), terminal.RETURN]
assert isinstance(terminal.prompt_input('hi'), six.text_type)
attr = Color.CYAN | curses.A_BOLD
stdscr.addstr.assert_called_with(39, 0, 'hi'.encode('ascii'), attr)
assert window.nlines == 1
assert window.ncols == 78
window.getch.side_effect = [ord('b'), ord('y'), ord('e'), terminal.ESCAPE]
assert terminal.prompt_input('hi') is None
stdscr.getch.side_effect = [ord('b'), ord('e'), terminal.RETURN]
assert terminal.prompt_input('hi', key=True) == ord('b')
stdscr.getch.side_effect = [terminal.ESCAPE, ord('e'), ord('l')]
assert terminal.prompt_input('hi', key=True) is None
def test_prompt_y_or_n(terminal, stdscr):
stdscr.getch.side_effect = [ord('y'), ord('N'), terminal.ESCAPE, ord('a')]
attr = Color.CYAN | curses.A_BOLD
# Press 'y'
assert terminal.prompt_y_or_n('hi')
stdscr.addstr.assert_called_with(39, 0, 'hi'.encode('ascii'), attr)
assert not curses.flash.called
# Press 'N'
assert not terminal.prompt_y_or_n('hi')
stdscr.addstr.assert_called_with(39, 0, 'hi'.encode('ascii'), attr)
assert not curses.flash.called
# Press Esc
assert not terminal.prompt_y_or_n('hi')
stdscr.addstr.assert_called_with(39, 0, 'hi'.encode('ascii'), attr)
assert not curses.flash.called
# Press an invalid key
assert not terminal.prompt_y_or_n('hi')
stdscr.addstr.assert_called_with(39, 0, 'hi'.encode('ascii'), attr)
assert curses.flash.called
def test_open_editor(terminal):
comment = COMMENT_EDIT_FILE.format(content='#| This is a comment! ❤')
data = {'filename': None}
def side_effect(args):
data['filename'] = args[1]
with codecs.open(data['filename'], 'r+', 'utf-8') as fp:
assert fp.read() == comment
fp.write('This is an amended comment! ❤')
return mock.Mock()
with mock.patch('subprocess.Popen', autospec=True) as Popen:
Popen.side_effect = side_effect
reply_text = terminal.open_editor(comment)
assert reply_text == 'This is an amended comment! ❤'
assert not os.path.isfile(data['filename'])
assert curses.endwin.called
assert curses.doupdate.called
def test_open_browser(terminal):
url = 'http://www.test.com'
terminal._display = True
with mock.patch('subprocess.check_call', autospec=True) as check_call:
terminal.open_browser(url)
assert check_call.called
assert not curses.endwin.called
assert not curses.doupdate.called
terminal._display = False
with mock.patch('webbrowser.open_new_tab', autospec=True) as open_new_tab:
terminal.open_browser(url)
open_new_tab.assert_called_with(url)
assert curses.endwin.called
assert curses.doupdate.called

11
tox.ini Normal file
View File

@@ -0,0 +1,11 @@
[tox]
envlist = py27,py34
[testenv]
deps =
pytest
mock
# Waiting for vcrpy to release https://github.com/kevin1024/vcrpy/pull/196
git+https://github.com/kevin1024/vcrpy.git
commands =
py.test -v {posargs}