Merge branch 'master' of https://github.com/TheoPib/rtv into TheoPib-master
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
RTV Changelog
|
||||
=============
|
||||
|
||||
.. _1.5: http://github.com/michael-lazar/rtv/releases/tag/v1.5
|
||||
.. _1.4.2: http://github.com/michael-lazar/rtv/releases/tag/v1.4.2
|
||||
.. _1.4.1: http://github.com/michael-lazar/rtv/releases/tag/v1.4.1
|
||||
.. _1.4: http://github.com/michael-lazar/rtv/releases/tag/v1.4
|
||||
@@ -10,6 +11,19 @@ RTV Changelog
|
||||
.. _1.2.1: http://github.com/michael-lazar/rtv/releases/tag/v1.2.1
|
||||
.. _1.2: http://github.com/michael-lazar/rtv/releases/tag/v1.2
|
||||
|
||||
-----------------
|
||||
1.5_ (2015-08-26)
|
||||
-----------------
|
||||
Features
|
||||
|
||||
* New page to view and open subscribed subreddits with `s`.
|
||||
* Sorting method can now be toggled with the `1` - `5` keys.
|
||||
* Links to x-posts are now opened inside of RTV.
|
||||
|
||||
Bugfixes
|
||||
|
||||
* Added */r/* to subreddit names in the subreddit view.
|
||||
|
||||
-------------------
|
||||
1.4.2_ (2015-08-01)
|
||||
-------------------
|
||||
|
||||
43
README.rst
43
README.rst
@@ -46,6 +46,13 @@ The installation will place a script in the system path
|
||||
$ rtv
|
||||
$ rtv --help
|
||||
|
||||
If you're having issues running RTV with Python 2, run RTV as module :
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd /path/to/rtv
|
||||
$ python2 -m rtv
|
||||
|
||||
=====
|
||||
Usage
|
||||
=====
|
||||
@@ -63,7 +70,7 @@ Basic Commands
|
||||
|
||||
:``j``/``k`` or ``▲``/``▼``: Move the cursor up/down
|
||||
:``m``/``n`` or ``PgUp``/``PgDn``: Jump to the previous/next page
|
||||
:``o`` or ``ENTER``: Open the selected item as a webpage
|
||||
:``1-5``: Toggle post order (*hot*, *top*, *rising*, *new*, *controversial*)
|
||||
:``r`` or ``F5``: Refresh page content
|
||||
:``u``: Log in or switch accounts
|
||||
:``?``: Show the help screen
|
||||
@@ -84,7 +91,8 @@ Once you are logged in your username will appear in the top-right corner of the
|
||||
:``c``: Compose a new post or comment
|
||||
:``e``: Edit an existing post or comment
|
||||
:``d``: Delete an existing post or comment
|
||||
:``s``: Open/close subscribed subreddits list
|
||||
:``i``: Display new messages prompt
|
||||
:``s``: View a list of subscribed subreddits
|
||||
|
||||
--------------
|
||||
Subreddit Mode
|
||||
@@ -93,6 +101,7 @@ Subreddit Mode
|
||||
In subreddit mode you can browse through the top submissions on either the front page or a specific subreddit.
|
||||
|
||||
:``l`` or ``►``: Enter the selected submission
|
||||
:``o`` or ``ENTER``: Open the submission link with your web browser
|
||||
:``/``: Open a prompt to switch subreddits
|
||||
:``f``: Open a prompt to search the current subreddit
|
||||
|
||||
@@ -111,6 +120,7 @@ Submission Mode
|
||||
In submission mode you can view the self text for a submission and browse comments.
|
||||
|
||||
:``h`` or ``◄``: Return to the subreddit
|
||||
:``o`` or ``ENTER``: Open the comment permalink with your web browser
|
||||
:``SPACE``: Fold the selected comment, or load additional comments
|
||||
|
||||
=============
|
||||
@@ -149,19 +159,14 @@ Config File
|
||||
-----------
|
||||
|
||||
RTV will read a configuration placed at ``~/.config/rtv/rtv.cfg`` (or ``$XDG_CONFIG_HOME``).
|
||||
Each line in the file will replace the corresponding default argument in the launch script.
|
||||
Each line in the files will replace the corresponding default argument in the launch script.
|
||||
This can be used to avoid having to re-enter login credentials every time the program is launched.
|
||||
|
||||
The OAuth section contains a boolean to trigger auto-login (defaults to False).
|
||||
When authenticated, two additional fields are written : **access_token** and **refresh_token**.
|
||||
Those are basically like username and password : they are used to authenticate you on Reddit servers.
|
||||
|
||||
Example initial config:
|
||||
|
||||
.. code-block:: ini
|
||||
**rtv.cfg**
|
||||
|
||||
[oauth]
|
||||
auto_login=False
|
||||
.. code-block:: ini
|
||||
|
||||
[rtv]
|
||||
# Log file location
|
||||
@@ -177,6 +182,24 @@ Example initial config:
|
||||
# This may be necessary for compatibility with some terminal browsers
|
||||
# ascii=True
|
||||
|
||||
-----
|
||||
OAuth
|
||||
-----
|
||||
|
||||
OAuth is an authentication standard, that replaces authentication with login and password.
|
||||
|
||||
RTV implements OAuth. It stores OAuth configuration at ``~/.config/rtv/oauth.cfg``(or ``$XDG_CONFIG_HOME``).
|
||||
**The OAuth configuration file must be writable, and is created automatically if it doesn't exist.**
|
||||
It contains a boolean to trigger auto-login (defaults to false).
|
||||
When authenticated, an additional field is written : **refresh_token**.
|
||||
This acts as a replacement to username and password : it is used to authenticate you on Reddit servers.
|
||||
|
||||
Example **oauth.cfg**:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[oauth]
|
||||
auto_login=false
|
||||
|
||||
=========
|
||||
Changelog
|
||||
|
||||
@@ -22,7 +22,13 @@ from tornado import ioloop
|
||||
|
||||
__all__ = []
|
||||
|
||||
def get_config_fp():
|
||||
def load_rtv_config():
|
||||
"""
|
||||
Attempt to load saved settings for things like the username and password.
|
||||
"""
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
HOME = os.path.expanduser('~')
|
||||
XDG_CONFIG_HOME = os.getenv('XDG_CONFIG_HOME',
|
||||
os.path.join(HOME, '.config'))
|
||||
@@ -35,29 +41,8 @@ def get_config_fp():
|
||||
# get the first existing config file
|
||||
for config_path in config_paths:
|
||||
if os.path.exists(config_path):
|
||||
break
|
||||
|
||||
return config_path
|
||||
|
||||
def open_config():
|
||||
"""
|
||||
Search for a configuration file at the location ~/.rtv and attempt to load
|
||||
saved settings for things like the username and password.
|
||||
"""
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
config_path = get_config_fp()
|
||||
config.read(config_path)
|
||||
|
||||
return config
|
||||
|
||||
def load_rtv_config():
|
||||
"""
|
||||
Attempt to load saved settings for things like the username and password.
|
||||
"""
|
||||
|
||||
config = open_config()
|
||||
break
|
||||
|
||||
defaults = {}
|
||||
if config.has_section('rtv'):
|
||||
@@ -73,14 +58,26 @@ def load_oauth_config():
|
||||
Attempt to load saved OAuth settings
|
||||
"""
|
||||
|
||||
config = open_config()
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
HOME = os.path.expanduser('~')
|
||||
XDG_CONFIG_HOME = os.getenv('XDG_CONFIG_HOME',
|
||||
os.path.join(HOME, '.config'))
|
||||
|
||||
if os.path.exists(os.path.join(XDG_CONFIG_HOME, 'rtv')):
|
||||
config_path = os.path.join(XDG_CONFIG_HOME, 'rtv', 'oauth.cfg')
|
||||
else:
|
||||
config_path = os.path.join(HOME, '.rtv-oauth')
|
||||
|
||||
config.read(config_path)
|
||||
|
||||
if config.has_section('oauth'):
|
||||
defaults = dict(config.items('oauth'))
|
||||
else:
|
||||
# Populate OAuth section
|
||||
config['oauth'] = {'auto_login': False}
|
||||
with open(get_config_fp(), 'w') as cfg:
|
||||
config.add_section('oauth')
|
||||
config.set('oauth', 'auto_login', 'false')
|
||||
with open(config_path, 'w') as cfg:
|
||||
config.write(cfg)
|
||||
defaults = dict(config.items('oauth'))
|
||||
|
||||
@@ -106,7 +103,6 @@ def command_line():
|
||||
|
||||
oauth_group = parser.add_argument_group('OAuth data (optional)', OAUTH)
|
||||
oauth_group.add_argument('--auto-login', dest='auto_login', help='OAuth auto-login setting')
|
||||
oauth_group.add_argument('--auth-token', dest='access_token', help='OAuth authorization token')
|
||||
oauth_group.add_argument('--refresh-token', dest='refresh_token', help='OAuth refresh token')
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -154,7 +150,7 @@ def main():
|
||||
reddit.config.decode_html_entities = False
|
||||
with curses_session() as stdscr:
|
||||
oauth = OAuthTool(reddit, stdscr, LoadScreen(stdscr))
|
||||
if args.auto_login == 'True': # Ew!
|
||||
if args.auto_login == 'true': # Ew!
|
||||
oauth.authorize()
|
||||
if args.link:
|
||||
page = SubmissionPage(stdscr, reddit, oauth, url=args.link)
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.4.2'
|
||||
__version__ = '1.5'
|
||||
|
||||
@@ -18,8 +18,8 @@ given, the program will display a secure prompt to enter a password.
|
||||
"""
|
||||
|
||||
OAUTH = """\
|
||||
Authentication is now done by OAuth, since PRAW will stop supporting login with
|
||||
username and password soon.
|
||||
Authentication is now done by OAuth, since PRAW will drop
|
||||
password authentication soon.
|
||||
"""
|
||||
|
||||
CONTROLS = """
|
||||
@@ -47,6 +47,7 @@ Authenticated Commands
|
||||
`c` : Compose a new post or comment
|
||||
`e` : Edit an existing post or comment
|
||||
`d` : Delete an existing post or comment
|
||||
`i` : Display new messages prompt
|
||||
`s` : Open/close subscribed subreddits list
|
||||
|
||||
Subreddit Mode
|
||||
|
||||
102
rtv/oauth.py
102
rtv/oauth.py
@@ -1,4 +1,3 @@
|
||||
from six.moves import configparser
|
||||
import curses
|
||||
import logging
|
||||
import os
|
||||
@@ -7,17 +6,17 @@ import uuid
|
||||
import webbrowser
|
||||
|
||||
import praw
|
||||
from six.moves import configparser
|
||||
|
||||
from . import config
|
||||
from .curses_helpers import show_notification, prompt_input
|
||||
|
||||
from tornado import ioloop, web
|
||||
from tornado import gen, ioloop, web, httpserver
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
__all__ = ['token_validity', 'OAuthTool']
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
token_validity = 3540
|
||||
|
||||
oauth_state = None
|
||||
oauth_code = None
|
||||
oauth_error = None
|
||||
@@ -29,6 +28,9 @@ class HomeHandler(web.RequestHandler):
|
||||
|
||||
class AuthHandler(web.RequestHandler):
|
||||
|
||||
def initialize(self):
|
||||
self.compact = os.environ.get('BROWSER') in ['w3m', 'links', 'elinks', 'lynx']
|
||||
|
||||
def get(self):
|
||||
global oauth_state
|
||||
global oauth_code
|
||||
@@ -40,6 +42,8 @@ class AuthHandler(web.RequestHandler):
|
||||
|
||||
self.render('auth.html', state=oauth_state, code=oauth_code, error=oauth_error)
|
||||
|
||||
# Stop IOLoop if using BackgroundBrowser (or GUI browser)
|
||||
if not self.compact:
|
||||
ioloop.IOLoop.current().stop()
|
||||
|
||||
class OAuthTool(object):
|
||||
@@ -59,34 +63,30 @@ class OAuthTool(object):
|
||||
self.redirect_uri = redirect_uri or config.oauth_redirect_uri
|
||||
|
||||
self.scope = scope or config.oauth_scope.split('-')
|
||||
|
||||
self.access_info = {}
|
||||
|
||||
self.token_expiration = 0
|
||||
# Terminal web browser
|
||||
self.compact = os.environ.get('BROWSER') in ['w3m', 'links', 'elinks', 'lynx']
|
||||
|
||||
# Initialize Tornado webapp and listen on port 65000
|
||||
# Initialize Tornado webapp
|
||||
self.callback_app = web.Application([
|
||||
(r'/', HomeHandler),
|
||||
(r'/auth', AuthHandler),
|
||||
], template_path='rtv/templates')
|
||||
self.callback_app.listen(65000)
|
||||
|
||||
self.http_server = None
|
||||
|
||||
def get_config_fp(self):
|
||||
HOME = os.path.expanduser('~')
|
||||
XDG_CONFIG_HOME = os.getenv('XDG_CONFIG_HOME',
|
||||
os.path.join(HOME, '.config'))
|
||||
|
||||
config_paths = [
|
||||
os.path.join(XDG_CONFIG_HOME, 'rtv', 'rtv.cfg'),
|
||||
os.path.join(HOME, '.rtv')
|
||||
]
|
||||
if os.path.exists(os.path.join(XDG_CONFIG_HOME, 'rtv')):
|
||||
file_path = os.path.join(XDG_CONFIG_HOME, 'rtv', 'oauth.cfg')
|
||||
else:
|
||||
file_path = os.path.join(HOME, '.rtv-oauth')
|
||||
|
||||
# get the first existing config file
|
||||
for config_path in config_paths:
|
||||
if os.path.exists(config_path):
|
||||
break
|
||||
|
||||
return config_path
|
||||
return file_path
|
||||
|
||||
def open_config(self, update=False):
|
||||
if self.config_fp is None:
|
||||
@@ -100,45 +100,47 @@ class OAuthTool(object):
|
||||
with open(self.config_fp, 'w') as cfg:
|
||||
self.config.write(cfg)
|
||||
|
||||
def set_token_expiration(self):
|
||||
self.token_expiration = time.time() + token_validity
|
||||
|
||||
def token_expired(self):
|
||||
return time.time() > self.token_expiration
|
||||
|
||||
def refresh(self, force=False):
|
||||
if self.token_expired() or force:
|
||||
try:
|
||||
with self.loader(message='Refreshing token'):
|
||||
new_access_info = self.reddit.refresh_access_information(
|
||||
self.config['oauth']['refresh_token'])
|
||||
self.access_info = new_access_info
|
||||
self.reddit.set_access_credentials(scope=set(self.access_info['scope']),
|
||||
access_token=self.access_info['access_token'],
|
||||
refresh_token=self.access_info['refresh_token'])
|
||||
self.set_token_expiration()
|
||||
except (praw.errors.OAuthAppRequired, praw.errors.OAuthInvalidToken,
|
||||
praw.errors.HTTPException) as e:
|
||||
show_notification(self.stdscr, ['Invalid OAuth data'])
|
||||
else:
|
||||
self.config['oauth']['access_token'] = self.access_info['access_token']
|
||||
self.config['oauth']['refresh_token'] = self.access_info['refresh_token']
|
||||
def clear_oauth_data(self):
|
||||
self.open_config(update=True)
|
||||
if self.config.has_section('oauth') and self.config.has_option('oauth', 'refresh_token'):
|
||||
self.config.remove_option('oauth', 'refresh_token')
|
||||
self.save_config()
|
||||
|
||||
@gen.coroutine
|
||||
def open_terminal_browser(self, url):
|
||||
with ThreadPoolExecutor(max_workers=1) as executor:
|
||||
yield executor.submit(webbrowser.open_new_tab, url)
|
||||
|
||||
ioloop.IOLoop.current().stop()
|
||||
|
||||
def authorize(self):
|
||||
if self.compact and not '.compact' in self.reddit.config.API_PATHS['authorize']:
|
||||
self.reddit.config.API_PATHS['authorize'] += '.compact'
|
||||
|
||||
self.reddit.set_oauth_app_info(self.client_id,
|
||||
self.client_secret,
|
||||
self.redirect_uri)
|
||||
|
||||
self.open_config(update=True)
|
||||
# If no previous OAuth data found, starting from scratch
|
||||
if 'oauth' not in self.config or 'access_token' not in self.config['oauth']:
|
||||
if not self.config.has_section('oauth') or not self.config.has_option('oauth', 'refresh_token'):
|
||||
if self.http_server is None:
|
||||
self.http_server = httpserver.HTTPServer(self.callback_app)
|
||||
self.http_server.listen(65000)
|
||||
|
||||
# Generate a random UUID
|
||||
hex_uuid = uuid.uuid4().hex
|
||||
|
||||
permission_ask_page_link = self.reddit.get_authorize_url(str(hex_uuid),
|
||||
scope=self.scope, refreshable=True)
|
||||
|
||||
if self.compact:
|
||||
show_notification(self.stdscr, ['Opening ' + os.environ.get('BROWSER')])
|
||||
curses.endwin()
|
||||
ioloop.IOLoop.current().add_callback(self.open_terminal_browser, permission_ask_page_link)
|
||||
ioloop.IOLoop.current().start()
|
||||
curses.doupdate()
|
||||
else:
|
||||
with self.loader(message='Waiting for authorization'):
|
||||
webbrowser.open(permission_ask_page_link)
|
||||
ioloop.IOLoop.current().start()
|
||||
@@ -169,21 +171,15 @@ class OAuthTool(object):
|
||||
with self.loader(message='Logging in'):
|
||||
# Get access information (tokens and scopes)
|
||||
self.access_info = self.reddit.get_access_information(self.final_code)
|
||||
|
||||
self.reddit.set_access_credentials(
|
||||
scope=set(self.access_info['scope']),
|
||||
access_token=self.access_info['access_token'],
|
||||
refresh_token=self.access_info['refresh_token'])
|
||||
self.set_token_expiration()
|
||||
except (praw.errors.OAuthAppRequired, praw.errors.OAuthInvalidToken) as e:
|
||||
show_notification(self.stdscr, ['Invalid OAuth data'])
|
||||
else:
|
||||
if 'oauth' not in self.config:
|
||||
self.config['oauth'] = {}
|
||||
if not self.config.has_section('oauth'):
|
||||
self.config.add_section('oauth')
|
||||
|
||||
self.config['oauth']['access_token'] = self.access_info['access_token']
|
||||
self.config['oauth']['refresh_token'] = self.access_info['refresh_token']
|
||||
self.config.set('oauth', 'refresh_token', self.access_info['refresh_token'])
|
||||
self.save_config()
|
||||
# Otherwise, fetch new access token
|
||||
else:
|
||||
self.refresh(force=True)
|
||||
with self.loader(message='Logging in'):
|
||||
self.reddit.refresh_access_information(self.config.get('oauth', 'refresh_token'))
|
||||
|
||||
16
rtv/page.py
16
rtv/page.py
@@ -314,9 +314,6 @@ class BasePage(object):
|
||||
|
||||
@BaseController.register('a')
|
||||
def upvote(self):
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
try:
|
||||
if 'likes' not in data:
|
||||
@@ -332,9 +329,6 @@ class BasePage(object):
|
||||
|
||||
@BaseController.register('z')
|
||||
def downvote(self):
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
try:
|
||||
if 'likes' not in data:
|
||||
@@ -358,6 +352,7 @@ class BasePage(object):
|
||||
|
||||
if self.reddit.is_oauth_session():
|
||||
self.reddit.clear_authentication()
|
||||
self.oauth.clear_oauth_data()
|
||||
return
|
||||
|
||||
self.oauth.authorize()
|
||||
@@ -372,9 +367,6 @@ class BasePage(object):
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
return
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
if data.get('author') != self.reddit.user.name:
|
||||
curses.flash()
|
||||
@@ -403,9 +395,6 @@ class BasePage(object):
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
return
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
if data.get('author') != self.reddit.user.name:
|
||||
curses.flash()
|
||||
@@ -440,9 +429,6 @@ class BasePage(object):
|
||||
Checks the inbox for unread messages and displays a notification.
|
||||
"""
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
inbox = len(list(self.reddit.get_unread(limit=1)))
|
||||
try:
|
||||
if inbox > 0:
|
||||
|
||||
@@ -93,9 +93,6 @@ class SubmissionPage(BasePage):
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
return
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
if data['type'] == 'Submission':
|
||||
content = data['text']
|
||||
@@ -131,9 +128,6 @@ class SubmissionPage(BasePage):
|
||||
def delete_comment(self):
|
||||
"Delete a comment as long as it is not the current submission"
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
if self.nav.absolute_index != -1:
|
||||
self.delete()
|
||||
else:
|
||||
|
||||
@@ -8,7 +8,7 @@ import requests
|
||||
from .exceptions import SubredditError, AccountError
|
||||
from .page import BasePage, Navigator, BaseController
|
||||
from .submission import SubmissionPage
|
||||
from .subscriptions import SubscriptionPage
|
||||
from .subscription import SubscriptionPage
|
||||
from .content import SubredditContent
|
||||
from .helpers import open_browser, open_editor, strip_subreddit_url
|
||||
from .docs import SUBMISSION_FILE
|
||||
@@ -54,9 +54,6 @@ class SubredditPage(BasePage):
|
||||
def refresh_content(self, name=None, order=None):
|
||||
"Re-download all submissions and reset the page index"
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
name = name or self.content.name
|
||||
order = order or self.content.order
|
||||
|
||||
@@ -136,9 +133,6 @@ class SubredditPage(BasePage):
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
return
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
# Strips the subreddit to just the name
|
||||
# Make sure it is a valid subreddit for submission
|
||||
subreddit = self.reddit.get_subreddit(self.content.name)
|
||||
@@ -180,9 +174,6 @@ class SubredditPage(BasePage):
|
||||
show_notification(self.stdscr, ['Not logged in'])
|
||||
return
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
# Open subscriptions page
|
||||
page = SubscriptionPage(self.stdscr, self.reddit, self.oauth)
|
||||
page.loop()
|
||||
|
||||
@@ -38,9 +38,6 @@ class SubscriptionPage(BasePage):
|
||||
def refresh_content(self):
|
||||
"Re-download all subscriptions and reset the page index"
|
||||
|
||||
# Refresh access token if expired
|
||||
self.oauth.refresh()
|
||||
|
||||
self.content = SubscriptionContent.from_user(self.reddit, self.loader)
|
||||
self.nav = Navigator(self.content.get)
|
||||
|
||||
10
setup.py
10
setup.py
@@ -1,6 +1,14 @@
|
||||
from setuptools import setup
|
||||
from version import __version__ as version
|
||||
|
||||
import sys
|
||||
|
||||
requirements = ['tornado', 'praw>=3.1.0', 'six', 'requests', 'kitchen']
|
||||
|
||||
# Python 2: add required concurrent.futures backport from Python 3.2
|
||||
if sys.version_info.major <= 2:
|
||||
requirements.append('futures')
|
||||
|
||||
setup(
|
||||
name='rtv',
|
||||
version=version,
|
||||
@@ -13,7 +21,7 @@ setup(
|
||||
keywords='reddit terminal praw curses',
|
||||
packages=['rtv'],
|
||||
include_package_data=True,
|
||||
install_requires=['tornado', 'praw>=3.1.0', 'six', 'requests', 'kitchen'],
|
||||
install_requires=requirements,
|
||||
entry_points={'console_scripts': ['rtv=rtv.__main__:main']},
|
||||
classifiers=[
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
|
||||
Reference in New Issue
Block a user