255 lines
9.3 KiB
Python
255 lines
9.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# pylint: disable=wrong-import-position
|
|
|
|
from __future__ import unicode_literals
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import sys
|
|
import locale
|
|
import logging
|
|
import warnings
|
|
|
|
import six
|
|
import requests
|
|
|
|
# Need to check for curses compatibility before performing the rtv imports
|
|
try:
|
|
import curses
|
|
except ImportError:
|
|
if sys.platform == 'win32':
|
|
sys.exit('Fatal Error: This program is not compatible with Windows '
|
|
'Operating Systems.\nPlease try installing on either Linux '
|
|
'or Mac OS')
|
|
else:
|
|
sys.exit('Fatal Error: Your python distribution appears to be missing '
|
|
'_curses.so.\nWas it compiled without support for curses?')
|
|
|
|
# If we want to override the $BROWSER variable that the python webbrowser
|
|
# references, it needs to be done before the webbrowser module is imported
|
|
# for the first time.
|
|
webbrowser_import_warning = ('webbrowser' in sys.modules)
|
|
RTV_BROWSER, BROWSER = os.environ.get('RTV_BROWSER'), os.environ.get('BROWSER')
|
|
if RTV_BROWSER:
|
|
os.environ['BROWSER'] = RTV_BROWSER
|
|
|
|
from . import docs
|
|
from . import packages
|
|
from .packages import praw
|
|
from .config import Config, copy_default_config, copy_default_mailcap
|
|
from .theme import Theme
|
|
from .oauth import OAuthHelper
|
|
from .terminal import Terminal
|
|
from .content import RequestHeaderRateLimiter
|
|
from .objects import curses_session, patch_webbrowser
|
|
from .subreddit_page import SubredditPage
|
|
from .exceptions import ConfigError, SubredditError
|
|
from .__version__ import __version__
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
# Pycharm debugging note:
|
|
# You can use pycharm to debug a curses application by launching rtv in a
|
|
# console window (python -m rtv) and using pycharm to attach to the remote
|
|
# process. On Ubuntu, you may need to allow ptrace permissions by setting
|
|
# ptrace_scope to 0 in /etc/sysctl.d/10-ptrace.conf.
|
|
# http://blog.mellenthin.de/archives/2010/10/18/gdb-attach-fails
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
|
|
# Squelch SSL warnings
|
|
logging.captureWarnings(True)
|
|
if six.PY3:
|
|
# These ones get triggered even when capturing warnings is turned on
|
|
warnings.simplefilter('ignore', ResourceWarning) # pylint:disable=E0602
|
|
|
|
# Set the terminal title
|
|
if os.getenv('DISPLAY'):
|
|
title = 'rtv {0}'.format(__version__)
|
|
sys.stdout.write('\x1b]2;{0}\x07'.format(title))
|
|
sys.stdout.flush()
|
|
|
|
args = Config.get_args()
|
|
fargs, bindings = Config.get_file(args.get('config'))
|
|
|
|
# Apply the file config first, then overwrite with any command line args
|
|
config = Config()
|
|
config.update(**fargs)
|
|
config.update(**args)
|
|
|
|
# If key bindings are supplied in the config file, overwrite the defaults
|
|
if bindings:
|
|
config.keymap.set_bindings(bindings)
|
|
|
|
if config['copy_config']:
|
|
copy_default_config()
|
|
return
|
|
if config['copy_mailcap']:
|
|
copy_default_mailcap()
|
|
return
|
|
|
|
if config['list_themes']:
|
|
Theme.print_themes()
|
|
return
|
|
|
|
# Load the browsing history from previous sessions
|
|
config.load_history()
|
|
|
|
# Load any previously saved auth session token
|
|
config.load_refresh_token()
|
|
if config['clear_auth']:
|
|
config.delete_refresh_token()
|
|
|
|
if config['log']:
|
|
# Log request headers to the file (print hack only works on python 3.x)
|
|
# from http import client
|
|
# _http_logger = logging.getLogger('http.client')
|
|
# client.HTTPConnection.debuglevel = 2
|
|
# def print_to_file(*args, **_):
|
|
# if args[0] != "header:":
|
|
# _http_logger.info(' '.join(args))
|
|
# client.print = print_to_file
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
filename=config['log'],
|
|
format='%(asctime)s:%(levelname)s:%(filename)s:%(lineno)d:%(message)s')
|
|
_logger.info('Starting new session, RTV v%s', __version__)
|
|
_logger.info('%s, %s', sys.executable, sys.version)
|
|
env = [
|
|
('$DISPLAY', os.getenv('DISPLAY')),
|
|
('$TERM', os.getenv('TERM')),
|
|
('$XDG_CONFIG_HOME', os.getenv('XDG_CONFIG_HOME')),
|
|
('$XDG_DATA_HOME', os.getenv('$XDG_DATA_HOME')),
|
|
('$RTV_EDITOR', os.getenv('RTV_EDITOR')),
|
|
('$RTV_URLVIEWER', os.getenv('RTV_URLVIEWER')),
|
|
('$RTV_BROWSER', RTV_BROWSER),
|
|
('$BROWSER', BROWSER),
|
|
('$PAGER', os.getenv('PAGER')),
|
|
('$VISUAL', os.getenv('VISUAL')),
|
|
('$EDITOR', os.getenv('EDITOR'))]
|
|
_logger.info('Environment: %s', env)
|
|
else:
|
|
# Add an empty handler so the logger doesn't complain
|
|
logging.root.addHandler(logging.NullHandler())
|
|
|
|
# Make sure the locale is UTF-8 for unicode support
|
|
default_locale = locale.setlocale(locale.LC_ALL, '')
|
|
try:
|
|
encoding = locale.getlocale()[1] or locale.getdefaultlocale()[1]
|
|
except ValueError:
|
|
# http://stackoverflow.com/a/19961403
|
|
# OS X on some terminals will set the LC_CTYPE to "UTF-8"
|
|
# (as opposed to something like "en_US.UTF-8") and python
|
|
# doesn't know how to handle it.
|
|
_logger.warning('Error parsing system locale: `%s`,'
|
|
' falling back to utf-8', default_locale)
|
|
encoding = 'UTF-8'
|
|
|
|
if not encoding or encoding.lower() != 'utf-8':
|
|
text = ('System encoding was detected as (%s) instead of UTF-8'
|
|
', falling back to ascii only mode' % encoding)
|
|
warnings.warn(text)
|
|
config['ascii'] = True
|
|
|
|
_logger.info('RTV module path: %s', os.path.abspath(__file__))
|
|
|
|
# Check the praw version
|
|
if packages.__praw_bundled__:
|
|
_logger.info('Using packaged PRAW distribution, '
|
|
'commit %s', packages.__praw_hash__)
|
|
else:
|
|
_logger.info('Packaged PRAW not found, falling back to system '
|
|
'installed version %s', praw.__version__)
|
|
|
|
# Update the webbrowser module's default behavior
|
|
patch_webbrowser()
|
|
if webbrowser_import_warning:
|
|
_logger.warning('webbrowser module was unexpectedly imported before'
|
|
'$BROWSER could be overwritten')
|
|
|
|
# Construct the reddit user agent
|
|
user_agent = docs.AGENT.format(version=__version__)
|
|
|
|
try:
|
|
with curses_session() as stdscr:
|
|
|
|
term = Terminal(stdscr, config)
|
|
|
|
if config['monochrome'] or config['theme'] == 'monochrome':
|
|
_logger.info('Using monochrome theme')
|
|
theme = Theme(use_color=False)
|
|
elif config['theme'] and config['theme'] != 'default':
|
|
_logger.info('Loading theme: %s', config['theme'])
|
|
theme = Theme.from_name(config['theme'])
|
|
else:
|
|
# Set to None to let the terminal figure out which theme
|
|
# to use depending on if colors are supported or not
|
|
theme = None
|
|
term.set_theme(theme)
|
|
|
|
with term.loader('Initializing', catch_exception=False):
|
|
reddit = praw.Reddit(user_agent=user_agent,
|
|
decode_html_entities=False,
|
|
disable_update_check=True,
|
|
timeout=10, # 10 second request timeout
|
|
handler=RequestHeaderRateLimiter())
|
|
|
|
# Dial the request cache up from 30 seconds to 5 minutes
|
|
# I'm trying this out to make navigation back and forth
|
|
# between pages quicker, it may still need to be fine tuned.
|
|
reddit.config.api_request_delay = 300
|
|
|
|
# Authorize on launch if the refresh token is present
|
|
oauth = OAuthHelper(reddit, term, config)
|
|
if config.refresh_token:
|
|
oauth.authorize()
|
|
|
|
page = None
|
|
name = config['subreddit']
|
|
with term.loader('Loading subreddit'):
|
|
try:
|
|
page = SubredditPage(reddit, term, config, oauth, name)
|
|
except Exception as e:
|
|
# If we can't load the subreddit that was requested, try
|
|
# to load the front page instead so at least the
|
|
# application still launches.
|
|
_logger.exception(e)
|
|
page = SubredditPage(reddit, term, config, oauth, 'front')
|
|
raise SubredditError('Unable to load {0}'.format(name))
|
|
if page is None:
|
|
return
|
|
|
|
# Open the supplied submission link before opening the subreddit
|
|
if config['link']:
|
|
# Expand shortened urls like https://redd.it/
|
|
# Praw won't accept the shortened versions, add the reddit
|
|
# headers to avoid a 429 response from reddit.com
|
|
url = requests.head(config['link'], headers=reddit.http.headers,
|
|
allow_redirects=True).url
|
|
|
|
page.open_submission(url=url)
|
|
|
|
# Launch the subreddit page
|
|
page.loop()
|
|
|
|
except ConfigError as e:
|
|
_logger.exception(e)
|
|
print(e)
|
|
except Exception as e:
|
|
_logger.exception(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()
|
|
|
|
|
|
sys.exit(main())
|