Merge pull request #278 from michael-lazar/remove_tornado

Remove tornado
This commit is contained in:
Michael Lazar
2016-08-10 02:16:34 -07:00
committed by GitHub
18 changed files with 1323 additions and 147 deletions

View File

@@ -5,8 +5,7 @@ python:
- "3.4"
- "3.5"
before_install:
- pip install coveralls pytest coverage mock pylint
- pip install git+https://github.com/kevin1024/vcrpy.git
- pip install coveralls pytest coverage mock pylint vcrpy
install:
- pip install .
script:

View File

@@ -9,7 +9,6 @@ import warnings
import six
import praw
import tornado
import requests
from . import docs
@@ -158,7 +157,5 @@ def main():
# 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)
sys.exit(main())

View File

@@ -15,9 +15,9 @@ from .objects import KeyMap
PACKAGE = os.path.dirname(__file__)
HOME = os.path.expanduser('~')
TEMPLATE = os.path.join(PACKAGE, 'templates')
DEFAULT_CONFIG = os.path.join(TEMPLATE, 'rtv.cfg')
DEFAULT_MAILCAP = os.path.join(TEMPLATE, 'mailcap')
TEMPLATES = os.path.join(PACKAGE, 'templates')
DEFAULT_CONFIG = os.path.join(TEMPLATES, 'rtv.cfg')
DEFAULT_MAILCAP = os.path.join(TEMPLATES, 'mailcap')
XDG_HOME = os.getenv('XDG_CONFIG_HOME', os.path.join(HOME, '.config'))
CONFIG = os.path.join(XDG_HOME, 'rtv', 'rtv.cfg')
MAILCAP = os.path.join(HOME, '.mailcap')

View File

@@ -103,3 +103,27 @@ SUBMISSION_EDIT_FILE = """{content}
#
# Editing {name}
"""
OAUTH_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.</p>
"""
OAUTH_ERROR = """\
<h1 style="color: red">Error</h1><hr>
<p>{error}</p>
"""
OAUTH_INVALID = """\
<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>
"""
OAUTH_SUCCESS = """\
<h1 style="color: green">Access Granted</h1><hr>
<p><span style="font-weight: bold">Reddit Terminal Viewer</span>
will now log in, you can close this window.</p>
"""

View File

@@ -1,62 +1,118 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import time
import uuid
import string
import codecs
import logging
import threading
from concurrent.futures import ThreadPoolExecutor
from tornado import gen, ioloop, web, httpserver
#pylint: disable=import-error
from six.moves.urllib.parse import urlparse, parse_qs
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from .config import TEMPLATE
from . import docs
from .config import TEMPLATES
class OAuthHandler(web.RequestHandler):
"""
Intercepts the redirect that Reddit sends the user to after they verify or
deny the application access.
_logger = logging.getLogger(__name__)
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.
"""
INDEX = os.path.join(TEMPLATES, 'index.html')
def initialize(self, display=None, params=None):
self.display = display
self.params = params
def get(self):
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)
class OAuthHandler(BaseHTTPRequestHandler):
self.render('index.html', **self.params)
# params are stored as a global because we don't have control over what
# gets passed into the handler __init__. These will be accessed by the
# OAuthHelper class.
params = {'state': None, 'code': None, 'error': None}
shutdown_on_request = True
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 self.display:
ioloop.IOLoop.current().stop()
def do_GET(self):
"""
Accepts GET requests to http://localhost:6500/, and stores the query
params in the global dict. If shutdown_on_request is true, stop the
server after the first successful request.
The http request may contain the following query params:
- state : unique identifier, should match what we passed to reddit
- code : code that can be exchanged for a refresh token
- error : if provided, the OAuth error that occurred
"""
parsed_path = urlparse(self.path)
if parsed_path.path != '/':
self.send_error(404)
qs = parse_qs(parsed_path.query)
self.params['state'] = qs['state'][0] if 'state' in qs else None
self.params['code'] = qs['code'][0] if 'code' in qs else None
self.params['error'] = qs['error'][0] if 'error' in qs else None
body = self.build_body()
# send_response also sets the Server and Date headers
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=UTF-8')
self.send_header('Content-Length', len(body))
self.end_headers()
self.wfile.write(body)
if self.shutdown_on_request:
# Shutdown the server after serving the request
# http://stackoverflow.com/a/22533929
thread = threading.Thread(target=self.server.shutdown)
thread.daemon = True
thread.start()
def log_message(self, format, *args):
"""
Redirect logging to our own handler instead of stdout
"""
_logger.debug(format, *args)
def build_body(self, template_file=INDEX):
"""
Params:
template_file (text): Path to an index.html template
Returns:
body (bytes): THe utf-8 encoded document body
"""
if self.params['error'] == 'access_denied':
message = docs.OAUTH_ACCESS_DENIED
elif self.params['error'] is not None:
message = docs.OAUTH_ERROR.format(error=self.params['error'])
elif self.params['state'] is None or self.params['code'] is None:
message = docs.OAUTH_INVALID
else:
message = docs.OAUTH_SUCCESS
with codecs.open(template_file, 'r', 'utf-8') as fp:
index_text = fp.read()
body = string.Template(index_text).substitute(message=message)
body = codecs.encode(body, 'utf-8')
return body
class OAuthHelper(object):
params = OAuthHandler.params
def __init__(self, reddit, term, config):
self.term = term
self.reddit = reddit
self.config = config
self.http_server = None
self.params = {'state': None, 'code': None, 'error': None}
# Initialize Tornado webapp
# 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=TEMPLATE)
# Wait to initialize the server, we don't want to reserve the port
# unless we know that the server needs to be used.
self.server = None
self.reddit.set_oauth_app_info(
self.config['oauth_client_id'],
@@ -79,40 +135,53 @@ class OAuthHelper(object):
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(self.config['oauth_redirect_port'])
state = uuid.uuid4().hex
authorize_url = self.reddit.get_authorize_url(
state, scope=self.config['oauth_scope'], refreshable=True)
if self.server is None:
address = ('', self.config['oauth_redirect_port'])
self.server = HTTPServer(address, OAuthHandler)
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.
# The server will block until it responds to its first request,
# at which point we can check the callback params.
OAuthHandler.shutdown_on_request = True
with self.term.loader('Opening browser for authorization'):
self.term.open_browser(authorize_url)
io.start()
self.server.serve_forever()
if self.term.loader.exception:
# Don't need to call server.shutdown() because serve_forever()
# is wrapped in a try-finally that doees it for us.
return
else:
# 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.
OAuthHandler.shutdown_on_request = False
with self.term.loader('Redirecting to reddit', delay=0):
# This load message exists to provide user feedback
time.sleep(1)
io.add_callback(self._async_open_browser, authorize_url)
io.start()
thread = threading.Thread(target=self.server.serve_forever)
thread.daemon = True
thread.start()
try:
self.term.open_browser(authorize_url)
except Exception as e:
# If an exception is raised it will be seen by the thread
# so we don't need to explicitly shutdown() the server
_logger.exception(e)
self.term.show_notification('Browser Error')
else:
self.server.shutdown()
finally:
thread.join()
if self.params['error'] == 'access_denied':
self.term.show_notification('Declined access')
self.term.show_notification('Denied access')
return
elif self.params['error']:
self.term.show_notification('Authentication error')
@@ -138,10 +207,4 @@ class OAuthHelper(object):
def clear_oauth_data(self):
self.reddit.clear_authentication()
self.config.delete_refresh_token()
@gen.coroutine
def _async_open_browser(self, url):
with ThreadPoolExecutor(max_workers=1) as executor:
yield executor.submit(self.term.open_browser, url)
ioloop.IOLoop.current().stop()
self.config.delete_refresh_token()

View File

@@ -25,20 +25,7 @@
</style>
</head>
<body>
{% 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 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 %}
<h1 style="color: green">Access Granted</h1><hr>
<p><span style="font-weight: bold">Reddit Terminal Viewer</span> will now log in, you can close this window.</p>
{% end %}
${message}
<div id="footer">View the <a href="http://www.github.com/michael-lazar/rtv">Documentation</a></div>
</body>
</html>

View File

@@ -728,7 +728,7 @@ class Terminal(object):
# space, assume that a newline operation was intended by the user
stack, current_line = [], ''
for line in text.split('\n'):
if line.endswith(' '):
if line.endswith(' ') or not line:
stack.append(current_line + line.rstrip())
current_line = ''
else:

View File

@@ -3,12 +3,9 @@ universal = 1
[metadata]
requires-dist =
tornado
praw>=3.5,<4
six
requests
kitchen
beautifulsoup4
mailcap-fix
futures; python_version=="2.6" or python_version=="2.7"

View File

@@ -3,12 +3,8 @@ import setuptools
from version import __version__ as version
requirements = ['tornado', 'praw==3.5.0', 'six', 'requests', 'kitchen',
'beautifulsoup4', 'mailcap-fix']
# Python 2: add required concurrent.futures backport from Python 3.2
if sys.version_info.major <= 2:
requirements.append('futures')
requirements = ['praw==3.5.0', 'six', 'requests', 'kitchen', 'beautifulsoup4',
'mailcap-fix']
setuptools.setup(
name='rtv',
@@ -23,8 +19,6 @@ setuptools.setup(
packages=['rtv'],
package_data={'rtv': ['templates/*']},
data_files=[("share/man/man1", ["rtv.1"])],
extras_require={
':python_version=="2.6" or python_version=="2.7"': ['futures']},
install_requires=requirements,
entry_points={'console_scripts': ['rtv=rtv.__main__:main']},
classifiers=[

View File

@@ -0,0 +1,963 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [rtv test suite PRAW/3.5.0 Python/3.4.0 b'Linux-3.13.0-92-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://api.reddit.com/r/python/.json?limit=1024
response:
body:
string: !!binary |
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
headers:
CF-RAY: [2d01eb8059cb282e-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['45307']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 10 Aug 2016 08:01:28 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d8b53ff1c55120830a38650f8114cbcc71470816087; expires=Thu,
10-Aug-17 08:01:27 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=g10byB%2Bscoc5lg0h0tH9suQqTZOnh80KOioCatbDVnM5WVDlaijt4zGOq0Qpa8UnGV4YE14t1fg%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: redirect_uri=http%3A%2F%2F127.0.0.1%3A65000%2F&refresh_token=**********&grant_type=refresh_token
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Authorization: ['**********']
Connection: [keep-alive]
Content-Length: ['122']
Content-Type: [application/x-www-form-urlencoded]
Cookie: [__cfduid=d8b53ff1c55120830a38650f8114cbcc71470816087]
User-Agent: [rtv test suite PRAW/3.5.0 Python/3.4.0 b'Linux-3.13.0-92-generic-x86_64-with-Ubuntu-14.04-trusty']
method: POST
uri: https://api.reddit.com/api/v1/access_token/
response:
body:
string: !!binary |
H4sIAAAAAAAAAyWMuwrCQBBFf2XY2kIjWtgpKax8oH3Y7F50IsnGmTEaxH+X1erC4Zz7dj4EqFaW
bujcily5vR9Oi/CMZbe+bU7HotmXs6Y4z3xyE3I/r7KxR5ZreIFkHjFwQMUx413qkCFePQu04vw8
X06nE3Ia0r9FZKMrqyUZiSM6YxupHfVRC2JkU+qFB29ooeovUBL4SII+iZH6AaSPumXLo0G4Bg3J
4D5f4B5kRtYAAAA=
headers:
CF-RAY: [2d01eb8b8a14282e-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 10 Aug 2016 08:01:29 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.5.0 Python/3.4.0 b'Linux-3.13.0-92-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/v1/me.json
response:
body:
string: !!binary |
H4sIAFvfqlcC/31V23LbQAj9lY6fO53EcZukP8OgFZIZrXbVvdhxMvn3wlqKVnXSNxtYOBwO6G3H
EWicrL8Q7X5/69BG+v5td8QII7KtTA5HjdgZPrHlV0zsHUxHfCV42InfBMJErYTcH/Y/9w/3vw53
P+40F7cEXfAjBN/4FKucUjzmOJFry8PFPKeCnMyS7m7/tKRTaL79F55lN8CAYUQx7vfPmt1BQ0n/
fyT240gufcQ9HsTYSbEcSHG9yRtJRMAj9gRToBPTOYL1fS94fE4SlELWXIF6jomC0CdIoLP+DImi
RrztThgYnf6Wmi4Fb2GvJNHLRIELBtaO9w/vBfuJ4IiTEMGuB+fPaxlsI4WTlAk0+ZDEv/r+C5bd
GkhjQ629gMHQLoFf4bz/DOfhueCkHs0FImEwR83TrSVG37AlOFMDCUNPW6TSBWA2qpna+EphgNZL
xwlSQDOszpjYDIL4OjEdzuzIUx9QFGW8H7hMbVMjeWGqy05Rzw4f0PUUSP2qdhlX9YzHPgfouZOM
ToiOG4xCBDpBSKctCsozAJi8ZXNZPZ0PRqaZ0lSFz+w42RoZdYPOUVi9kx9o9K73CiFRmcdmNjE3
Ar/l9OlsHnU2n7Moim28kAFGQA7FuVVQkU6W/Rf2VrOj8ywj2SGD5njbZjQiFnvDS+lbJtByIFNt
i4QTOdXdzZMJcyTY1O/LQsQUQWa9CVZk102AllEw1hzj3P+mxQ/uIGRb6yUJh8LP7ZQiJVVv5bhI
XG7EYwJO9eRQJH3ipDsRTixzPwdOeiYX/6JJepG5VpqcfExQ1rIKlsCr3mAUAhFW6DOkOnFjvRmW
i/MVHDmkSwEViddDcv+0PpAD3Htb314u17U26J+d/ePQq/o0XLqZOJQvgPhctlbjXONfRMC5SHa5
1FKQO5ZzVNauSlvymGt7yrQ+uH4LdAGrCvNXQKu8/wUqtT8UrwYAAA==
headers:
CF-RAY: [2d01eb99c3b50651-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['724']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 10 Aug 2016 08:01:31 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=d2eed5c921f4a1e4bd4cb46e8350605a71470816091; expires=Thu,
10-Aug-17 08:01:31 GMT; path=/; domain=.reddit.com; HttpOnly']
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: ['598.0']
x-ratelimit-reset: ['509']
x-ratelimit-used: ['2']
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=d2eed5c921f4a1e4bd4cb46e8350605a71470816091]
User-Agent: [rtv test suite PRAW/3.5.0 Python/3.4.0 b'Linux-3.13.0-92-generic-x86_64-with-Ubuntu-14.04-trusty']
method: GET
uri: https://oauth.reddit.com/api/multi/mine/.json
response:
body:
string: !!binary |
H4sIAF3fqlcC/3VUbUsbQRD+K9crSAvGKNEilX5IlFCotkIC/WDk2NudZIfsG/sSOcX/3tm8qHdp
vhx7zzMzz8zszjy8lEs0ovxelLesBgXiLqmI5XFRChYZ4S8lZ6YCgZF+ok+QKQxOsaYyTEN29SCI
dxBDdvw/KiBwjy6iNZWMWmWLIxWvPvV6xeS6+jMeF73e0SJeZVDgquCKhfBjVmoxK3e4y4d7aaMN
hZ0XG4UiS5wUR0y7K7IZZMP+2nJmZubNjVuHIIq5t7rIICukhzkJ9FMA39/E6uvtIcfc6B6k1zps
l9pOcX2m/Hf4rr7f2/JyL7gHFiF3/Wzw7fLibDC4OD85zcQ6xyrnmPtzUDoHQU6dTD430iSlCAmp
3pgEwh5e3i5C24W2RrCmfD0u3uEnjwsFzcIaeEIl2uQkglIsSphE5tsUvQdjrVBQpxjb1LVEpZq/
+dMm7pEvFVxLgAA31qSO3zB5a4ZG/MQwgj3npGtvQyc/G8h+KmGKpmM+VMhhQ96kuoZO+ioZ5pJz
Ha9gRUNwG/TMCKtJHKFN5NgMVTOiD3QiTW3dQXhynC2Bs5j83jUE6ySCs5Hp1CnyxvqO8USCCMNb
G6m+33ZK6m1+aU6jRLMIv5jXrM2NrKZmUOYjei7CLrql6gCBLnyvCXXyHqPN74QnIbq3wyXdrYOO
mGPRE57j0XuVzC/bfJOW6JB3CnZgTEM9d7ZrP2YmsrHHvfZNsu0dGtorFOzxfbqqFHm5mbDzy/OL
7YQtoam4VdZn388cYDC/zNO0woA1KozNOg+PK4rxNmY7sQw8AS5kpB5XgUvY9iDvKuSZpsJlhjaz
y3FFQZ/Zeu05yZ6hGuxN8sfVqNe7+NCKy+stL7WPu+zh4JZ4/HKQ+lq+vj7+A1SYhRP9BQAA
headers:
CF-RAY: [2d01eba4a40a0651-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['681']
Content-Type: [application/json; charset=UTF-8]
Date: ['Wed, 10 Aug 2016 08:01:33 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: ['597.0']
x-ratelimit-reset: ['508']
x-ratelimit-used: ['3']
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=8vk4hb0j64upFrGIvLcAUI0QdaP%2BF2nE7aCdt4yoQYWLsaxTRrj4KRlbnOCUYnkjvOM604a5yR5dO6vf6XcEAAZ2BNQ%2BDxMd']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -4,14 +4,16 @@ from __future__ import unicode_literals
import os
import curses
import logging
import threading
from functools import partial
import praw
import pytest
from vcr import VCR
from six.moves.urllib.parse import urlparse, parse_qs
from six.moves.BaseHTTPServer import HTTPServer
from rtv.oauth import OAuthHelper
from rtv.oauth import OAuthHelper, OAuthHandler
from rtv.config import Config
from rtv.terminal import Terminal
from rtv.subreddit import SubredditPage
@@ -196,6 +198,21 @@ def oauth(reddit, terminal, config):
return OAuthHelper(reddit, terminal, config)
@pytest.yield_fixture()
def oauth_server():
# Start the OAuth server on a random port in the background
server = HTTPServer(('', 0), OAuthHandler)
server.url = 'http://{0}:{1}/'.format(*server.server_address)
thread = threading.Thread(target=server.serve_forever)
thread.start()
try:
yield server
finally:
server.shutdown()
thread.join()
server.server_close()
@pytest.fixture()
def submission_page(reddit, terminal, config, oauth):
submission = 'https://www.reddit.com/r/Python/comments/2xmo63'

View File

@@ -25,6 +25,25 @@ def test_copy_default_config():
assert permissions == 0o664
def test_copy_default_config_cancel():
"Pressing ``n`` should cancel the copy"
with NamedTemporaryFile(suffix='.cfg') as fp:
with mock.patch('rtv.config.six.moves.input', return_value='n'):
copy_default_config(fp.name)
assert not fp.read()
def test_copy_config_interrupt():
"Pressing ``Ctrl-C`` should cancel the copy"
with NamedTemporaryFile(suffix='.cfg') as fp:
with mock.patch('rtv.config.six.moves.input') as func:
func.side_effect = KeyboardInterrupt
copy_default_config(fp.name)
assert not fp.read()
def test_copy_default_mailcap():
"Make sure the example mailcap file was included in the package"
@@ -180,6 +199,11 @@ def test_config_refresh_token():
def test_config_history():
"Ensure that the history can be loaded and saved"
# Should still be able to load if the file doesn't exist
config = Config(history_file='/fake_path/fake_file')
config.load_history()
assert len(config.history) == 0
with NamedTemporaryFile(delete=False) as fp:
config = Config(history_file=fp.name, history_size=3)

View File

@@ -6,13 +6,16 @@ from itertools import islice
import six
import praw
import mock
import pytest
from rtv.content import (
Content, SubmissionContent, SubredditContent, SubscriptionContent)
from rtv import exceptions
try:
from unittest import mock
except ImportError:
import mock
# Test entering a bunch of text into the prompt
# (text, parsed subreddit, parsed order)

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from tornado.web import Application
from tornado.testing import AsyncHTTPTestCase
import requests
from praw.errors import OAuthException
from rtv.oauth import OAuthHelper, OAuthHandler
from rtv.config import TEMPLATE
try:
from unittest import mock
@@ -14,38 +13,48 @@ except ImportError:
import mock
class TestAuthHandler(AsyncHTTPTestCase):
def test_oauth_handler_not_found(oauth_server):
def get_app(self):
self.params = {}
handler = [('/', OAuthHandler, {'params': self.params})]
return Application(handler, template_path=TEMPLATE)
url = oauth_server.url + 'favicon.ico'
resp = requests.get(url)
assert resp.status_code == 404
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_oauth_handler_no_callback(oauth_server):
def test_error(self):
resp = self.fetch('/?error=fake')
assert resp.code == 200
assert self.params['error'] == 'fake'
assert 'fake' in resp.body.decode()
resp = requests.get(oauth_server.url)
assert resp.status_code == 200
assert 'Wait...' in resp.text
assert OAuthHandler.params['error'] is None
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_handler_access_denied(oauth_server):
url = oauth_server.url + '?error=access_denied'
resp = requests.get(url)
assert resp.status_code == 200
assert OAuthHandler.params['error'] == 'access_denied'
assert 'denied access' in resp.text
def test_oauth_handler_error(oauth_server):
url = oauth_server.url + '?error=fake'
resp = requests.get(url)
assert resp.status_code == 200
assert OAuthHandler.params['error'] == 'fake'
assert 'fake' in resp.text
def test_oauth_handler_success(oauth_server):
url = oauth_server.url + '?state=fake_state&code=fake_code'
resp = requests.get(url)
assert resp.status_code == 200
assert OAuthHandler.params['error'] is None
assert OAuthHandler.params['state'] == 'fake_state'
assert OAuthHandler.params['code'] == 'fake_code'
assert 'Access Granted' in resp.text
def test_oauth_terminal_non_mobile_authorize(reddit, terminal, config):
@@ -66,11 +75,11 @@ def test_oauth_terminal_mobile_authorize(reddit, terminal, config):
assert '.compact' in oauth.reddit.config.API_PATHS['authorize']
def test_oauth_authorize_with_refresh_token(oauth, stdscr, refresh_token):
def test_oauth_authorize_with_refresh_token(oauth, refresh_token):
oauth.config.refresh_token = refresh_token
oauth.authorize()
assert oauth.http_server is None
assert oauth.server is None
# We should be able to handle an oauth failure
with mock.patch.object(oauth.reddit, 'refresh_access_information'):
@@ -78,7 +87,15 @@ def test_oauth_authorize_with_refresh_token(oauth, stdscr, refresh_token):
oauth.reddit.refresh_access_information.side_effect = exception
oauth.authorize()
assert isinstance(oauth.term.loader.exception, OAuthException)
assert oauth.http_server is None
assert oauth.server is None
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
def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
@@ -87,34 +104,36 @@ def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
# 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('rtv.oauth.HTTPServer') as http_server, \
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)
def serve_forever():
oauth.params.update(**params)
http_server.return_value.serve_forever.side_effect = serve_forever
oauth.authorize()
assert not open_browser.called
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.save_refresh_token.reset_mock()
oauth.http_server = None
oauth.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
assert oauth.server is None
stdscr.reset_mock()
# Invalid state returned
@@ -129,7 +148,6 @@ def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
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
@@ -137,11 +155,12 @@ def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
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
oauth.server = None
# Exceptions when logging in are handled correctly
with mock.patch.object(oauth.reddit, 'get_access_information'):
@@ -149,13 +168,4 @@ def test_oauth_authorize(oauth, reddit, stdscr, refresh_token):
oauth.reddit.get_access_information.side_effect = exception
oauth.authorize()
assert isinstance(oauth.term.loader.exception, OAuthException)
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
assert not oauth.config.save_refresh_token.called

View File

@@ -3,6 +3,7 @@ from __future__ import unicode_literals
import time
import curses
from collections import OrderedDict
import six
import pytest
@@ -314,7 +315,13 @@ def test_objects_controller_command():
ControllerA.character_map = {Command('REFRESH'): 0, Command('UPVOTE'): 0}
# A double command can't share the first key with a single comand
keymap = KeyMap({'REFRESH': ['gg'], 'UPVOTE': ['g']})
keymap = KeyMap(OrderedDict([('REFRESH', ['gg']), ('UPVOTE', ['g'])]))
with pytest.raises(exceptions.ConfigError) as e:
ControllerA(None, keymap=keymap)
assert 'ControllerA' in six.text_type(e)
# It doesn't matter which order they were entered
keymap = KeyMap(OrderedDict([('UPVOTE', ['g']), ('REFRESH', ['gg'])]))
with pytest.raises(exceptions.ConfigError) as e:
ControllerA(None, keymap=keymap)
assert 'ControllerA' in six.text_type(e)

View File

@@ -22,6 +22,21 @@ def test_submission_page_construct(reddit, terminal, config, oauth):
# Toggle the second comment so we can check the draw more comments method
page.content.toggle(1)
# Set some special flags to make sure that we can draw them
submission_data = page.content.get(-1)
submission_data['gold'] = True
submission_data['stickied'] = True
submission_data['saved'] = True
submission_data['flair'] = 'flair'
# Set some special flags to make sure that we can draw them
comment_data = page.content.get(0)
comment_data['gold'] = True
comment_data['stickied'] = True
comment_data['saved'] = True
comment_data['flair'] = 'flair'
page.draw()
# Title
@@ -70,6 +85,14 @@ def test_submission_refresh(submission_page):
submission_page.refresh_content()
def test_submission_exit(submission_page):
# Exiting should set active to false
submission_page.active = True
submission_page.controller.trigger('h')
assert not submission_page.active
def test_submission_unauthenticated(submission_page, terminal):
# Unauthenticated commands
@@ -179,6 +202,16 @@ def test_submission_vote(submission_page, refresh_token):
assert upvote.called
assert data['likes'] is True
# Clear vote
submission_page.controller.trigger('a')
assert clear_vote.called
assert data['likes'] is None
# Upvote
submission_page.controller.trigger('a')
assert upvote.called
assert data['likes'] is True
# Downvote
submission_page.controller.trigger('z')
assert downvote.called
@@ -271,7 +304,6 @@ def test_submission_comment(submission_page, terminal, refresh_token):
mock.patch.object(terminal, 'open_editor') as open_editor, \
mock.patch('time.sleep'):
open_editor.return_value.__enter__.return_value = 'comment text'
submission_page.controller.trigger('c')
assert open_editor.called
add_comment.assert_called_with('comment text')

View File

@@ -195,12 +195,24 @@ def test_subreddit_open_subscriptions(subreddit_page, refresh_token):
subreddit_page.config.refresh_token = refresh_token
subreddit_page.oauth.authorize()
# Open a subscription
# Open subscriptions
with mock.patch('rtv.page.Page.loop') as loop:
subreddit_page.controller.trigger('s')
assert loop.called
def test_subreddit_open_multireddits(subreddit_page, refresh_token):
# Log in
subreddit_page.config.refresh_token = refresh_token
subreddit_page.oauth.authorize()
# Open multireddits
with mock.patch('rtv.page.Page.loop') as loop:
subreddit_page.controller.trigger('S')
assert loop.called
def test_subreddit_draw_header(subreddit_page, refresh_token, terminal):
# /r/front alias should be renamed in the header

View File

@@ -389,6 +389,7 @@ def test_open_link_subprocess(terminal):
with mock.patch('time.sleep'), \
mock.patch('os.system'), \
mock.patch('subprocess.Popen') as Popen, \
mock.patch('six.moves.input') as six_input, \
mock.patch.object(terminal, 'get_mailcap_entry'):
@@ -398,6 +399,9 @@ def test_open_link_subprocess(terminal):
six_input.reset_mock()
os.system.reset_mock()
terminal.stdscr.subwin.addstr.reset_mock()
Popen.return_value.communicate.return_value = '', 'stderr message'
Popen.return_value.poll.return_value = 0
Popen.return_value.wait.return_value = 0
def get_error():
# Check if an error message was printed to the terminal
@@ -415,6 +419,8 @@ def test_open_link_subprocess(terminal):
# Non-blocking failure
reset_mock()
Popen.return_value.poll.return_value = 127
Popen.return_value.wait.return_value = 127
entry = ('fake .', 'fake %s')
terminal.get_mailcap_entry.return_value = entry
terminal.open_link(url)
@@ -431,6 +437,8 @@ def test_open_link_subprocess(terminal):
# needsterminal failure
reset_mock()
Popen.return_value.poll.return_value = 127
Popen.return_value.wait.return_value = 127
entry = ('fake .', 'fake %s; needsterminal')
terminal.get_mailcap_entry.return_value = entry
terminal.open_link(url)
@@ -447,6 +455,8 @@ def test_open_link_subprocess(terminal):
# copiousoutput failure
reset_mock()
Popen.return_value.poll.return_value = 127
Popen.return_value.wait.return_value = 127
entry = ('fake .', 'fake %s; needsterminal; copiousoutput')
terminal.get_mailcap_entry.return_value = entry
terminal.open_link(url)
@@ -495,3 +505,40 @@ def test_open_pager(terminal, stdscr):
terminal.open_pager(data)
message = 'Could not open pager fake'.encode('ascii')
assert stdscr.addstr.called_with(0, 0, message)
def test_open_urlview(terminal, stdscr):
data = "Hello World! ❤"
def side_effect(args, stdin=None):
assert stdin is not None
raise OSError
with mock.patch('subprocess.Popen') as Popen, \
mock.patch.dict('os.environ', {'RTV_URLVIEWER': 'fake'}):
Popen.return_value.poll.return_value = 0
terminal.open_urlview(data)
assert Popen.called
assert not stdscr.addstr.called
Popen.return_value.poll.return_value = 1
terminal.open_urlview(data)
assert stdscr.subwin.addstr.called
# Raise an OS error
Popen.side_effect = side_effect
terminal.open_urlview(data)
message = 'Failed to open fake'.encode('utf-8')
assert stdscr.addstr.called_with(0, 0, message)
def test_strip_textpad(terminal):
assert terminal.strip_textpad(None) is None
assert terminal.strip_textpad(' foo ') == ' foo'
text = 'alpha bravo\ncharlie \ndelta \n echo \n\nfoxtrot\n\n\n'
assert terminal.strip_textpad(text) == (
'alpha bravocharlie delta\n echo\n\nfoxtrot')