Raise NotImplemetedError.

This commit is contained in:
Michael Lazar
2016-08-04 01:01:48 -07:00
parent 3b3c74cf72
commit 7a34c089b9
9 changed files with 5537 additions and 5072 deletions

View File

@@ -15,9 +15,17 @@ from . import exceptions
class Content(object):
def get(self, index, n_cols):
"""
Grab the item at the given index, and format the text to fit a width of
n columns.
"""
raise NotImplementedError
def iterate(self, index, step, n_cols=70):
"""
Return an iterator that starts and the current index and increments
by the given step.
"""
while True:
if step < 0 and index < 0:
@@ -30,6 +38,13 @@ class Content(object):
break
index += step
@property
def range(self):
"""
Return the minimm and maximum valid indicies.
"""
raise NotImplementedError
@staticmethod
def flatten_comments(comments, root_level=0):
"""
@@ -305,6 +320,10 @@ class SubmissionContent(Content):
submission = reddit.get_submission(url, comment_sort=order)
return cls(submission, loader, indent_size, max_indent_level, order)
@property
def range(self):
return -1, len(self._comment_data) - 1
def get(self, index, n_cols=70):
"""
Grab the `i`th submission, with the title field formatted to fit inside
@@ -559,6 +578,13 @@ class SubredditContent(Content):
# We made it!
return cls(display_name, submissions, loader, order=display_order)
@property
def range(self):
# Note that for subreddits, the submissions are generated lazily and
# there is no actual "end" index. Instead, we return the bottom index
# that we have loaded so far.
return 0, len(self._submission_data) - 1
def get(self, index, n_cols=70):
"""
Grab the `i`th submission, with the title field formatted to fit inside
@@ -615,6 +641,15 @@ class SubscriptionContent(Content):
except IndexError:
raise exceptions.SubscriptionError('No content')
# Load 1024 subscriptions up front (one http request's worth)
# For most people this should be all of their subscriptions. Doing thi
# allows the user to jump to the end of the page with `G`.
if name != 'Popular Subreddits':
try:
self.get(1023)
except IndexError:
pass
@classmethod
def from_user(cls, reddit, loader, content_type='subreddit'):
if content_type == 'subreddit':
@@ -632,6 +667,10 @@ class SubscriptionContent(Content):
return cls(name, items, loader)
@property
def range(self):
return 0, len(self._subscription_data) - 1
def get(self, index, n_cols=70):
"""
Grab the `i`th object, with the title field formatted to fit

View File

@@ -519,11 +519,6 @@ class Controller(object):
>>> def upvote(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)
@@ -538,6 +533,8 @@ class Controller(object):
# 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]
# Keep track of last key press for doubles like `gg`
self.last_char = None
if not keymap:
return
@@ -551,6 +548,18 @@ class Controller(object):
if isinstance(command, Command):
for key in keymap.get(command):
val = keymap.parse(key)
# If a double key press is defined, the first half
# must be unbound
if isinstance(val, tuple):
if controller.character_map.get(val[0]) is not None:
raise exceptions.ConfigError(
"Invalid configuration! `%s` is bound to "
"duplicate commands in the "
"%s" % (key, controller.__name__))
# Mark the first half of the double with None so
# that no other command can use it
controller.character_map[val[0]] = None
# Check if the key is already programmed to trigger a
# different function.
if controller.character_map.get(val, func) != func:
@@ -569,16 +578,19 @@ class Controller(object):
# Check if the controller (or any of the controller's parents) have
# registered a function to the given key
for controller in self.parents:
func = controller.character_map.get((self.last_char, char))
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
if func:
self.last_char = None
return func(self.instance, *args, **kwargs)
else:
self.last_char = char
return None
@classmethod
def register(cls, *chars):
@@ -645,8 +657,8 @@ class KeyMap(object):
raise exceptions.ConfigError('Invalid configuration! `%s` key is '
'undefined' % command.val)
@staticmethod
def parse(key):
@classmethod
def parse(cls, key):
"""
Parse a key represented by a string and return its character code.
"""
@@ -663,6 +675,9 @@ class KeyMap(object):
elif key.startswith('0x'):
# Ascii hex code
return int(key, 16)
elif len(key) == 2:
# Double presses
return tuple(cls.parse(k) for k in key)
else:
# Ascii character
code = ord(key)

View File

@@ -124,6 +124,22 @@ class Page(object):
self._move_page(1)
self.clear_input_queue()
@PageController.register(Command('PAGE_TOP'))
def move_page_top(self):
self._remove_cursor()
self.nav.page_index = self.content.range[0]
self.nav.cursor_index = 0
self.nav.inverted = False
self._add_cursor()
@PageController.register(Command('PAGE_BOTTOM'))
def move_page_bottom(self):
self._remove_cursor()
self.nav.page_index = self.content.range[1]
self.nav.cursor_index = 0
self.nav.inverted = True
self._add_cursor()
@PageController.register(Command('UPVOTE'))
@logged_in
def upvote(self):

View File

@@ -101,6 +101,8 @@ MOVE_UP = k, <KEY_UP>
MOVE_DOWN = j, <KEY_DOWN>
PAGE_UP = m, <KEY_PPAGE>, <NAK>
PAGE_DOWN = n, <KEY_NPAGE>, <EOT>
PAGE_TOP = gg
PAGE_BOTTOM = G
UPVOTE = a
DOWNVOTE = z
LOGIN = u

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.5.0 Python/3.4.0 b'Linux-3.13.0-92-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-RAY: [2cd06f2da43a2894-SJC]
Connection: [keep-alive]
Content-Encoding: [gzip]
Content-Length: ['7674']
Content-Type: [application/json; charset=UTF-8]
Date: ['Thu, 04 Aug 2016 07:53:12 GMT']
Server: [cloudflare-nginx]
Set-Cookie: ['__cfduid=deaa9dbd54fbd8a077e39b5959dec2bc01470297192; expires=Fri,
04-Aug-17 07:53:12 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=3mNLLiHWXXIBcSfLlY62cXrSkjuA13zPmWYuKQQ2GBZ40AE3i97WARhJi%2FzYCgZC7sGW2UGKac4%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -172,7 +172,7 @@ def test_content_submission(reddit, terminal):
content = SubmissionContent(submission, terminal.loader)
# Everything is loaded upon instantiation
assert len(content._comment_data) == 45
assert content.range == (-1, 44)
assert content.get(-1)['type'] == 'Submission'
assert content.get(40)['type'] == 'Comment'
@@ -200,13 +200,13 @@ def test_content_submission(reddit, terminal):
assert data['count'] == 3
assert data['hidden'] is True
assert data['level'] >= content.get(3)['level']
assert len(content._comment_data) == 43
assert content.range == (-1, 42)
# Toggling again expands the children
content.toggle(2)
data = content.get(2)
assert data['hidden'] is False
assert len(content._comment_data) == 45
assert content.range == (-1, 44)
def test_content_submission_load_more_comments(reddit, terminal):
@@ -219,7 +219,8 @@ def test_content_submission_load_more_comments(reddit, terminal):
# More comments load when toggled
assert content.get(390)['type'] == 'MoreComments'
content.toggle(390)
assert len(content._comment_data) > 390
assert content.range[0] == -1
assert content.range[1] > 390
assert content.get(390)['type'] == 'Comment'
@@ -254,7 +255,7 @@ def test_content_subreddit_initialize(reddit, terminal):
content = SubredditContent('python', submissions, terminal.loader, 'top')
assert content.name == 'python'
assert content.order == 'top'
assert len(content._submission_data) == 1
assert content.range == (0, 0)
def test_content_subreddit_initialize_invalid(reddit, terminal):
@@ -271,7 +272,7 @@ def test_content_subreddit(reddit, terminal):
content = SubredditContent('front', submissions, terminal.loader)
# Submissions are loaded on demand, excluding for the first one
assert len(content._submission_data) == 1
assert content.range == (0, 0)
assert content.get(0)['type'] == 'Submission'
for data in content.iterate(0, 1):
@@ -295,7 +296,7 @@ def test_content_subreddit_load_more(reddit, terminal):
content = SubredditContent('front', submissions, terminal.loader)
assert content.get(50)['type'] == 'Submission'
assert len(content._submission_data) == 51
assert content.range == (0, 50)
for i, data in enumerate(islice(content.iterate(0, 1), 0, 50)):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type',
@@ -416,6 +417,7 @@ def test_content_subscription(reddit, terminal):
# These are static
assert content.name == 'Popular Subreddits'
assert content.order is None
assert content.range == (0, 0)
# Validate content
for data in islice(content.iterate(0, 1), 20):
@@ -425,6 +427,8 @@ def test_content_subscription(reddit, terminal):
for val in data.values():
assert not isinstance(val, six.binary_type)
assert content.range == (0, 19)
def test_content_subreddit_saved(reddit, oauth, refresh_token, terminal):

View File

@@ -249,6 +249,41 @@ def test_objects_controller():
assert controller_c.trigger('3') is None
def test_objects_controller_double_press():
class ControllerA(Controller):
character_map = {}
@ControllerA.register(Command('F1'))
def call_page(_):
return '1'
@ControllerA.register(Command('F2'))
def call_page(_):
return '2'
keymap = KeyMap({'F1': ['gg'], 'F2': ['a']})
controller_a = ControllerA(None, keymap=keymap)
# Double press
controller_a.last_char = None
assert controller_a.trigger('g') is None
assert controller_a.trigger('g') == '1'
assert controller_a.trigger('g') is None
# Executing another command cancels the chain
controller_a.last_char = None
assert controller_a.trigger('g') is None
assert controller_a.trigger('a') == '2'
assert controller_a.trigger('g') is None
# Pressing an invalid key cancels the chain
controller_a.last_char = None
assert controller_a.trigger('g') is None
assert controller_a.trigger('b') is None
assert controller_a.trigger('g') is None
def test_objects_controller_command():
class ControllerA(Controller):
@@ -278,6 +313,15 @@ def test_objects_controller_command():
# Reset the character map
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']})
with pytest.raises(exceptions.ConfigError) as e:
ControllerA(None, keymap=keymap)
assert 'ControllerA' in six.text_type(e)
# Reset the character map
ControllerA.character_map = {Command('REFRESH'): 0, Command('UPVOTE'): 0}
# All commands must be defined in the keymap
keymap = KeyMap({'REFRESH': [0x10]})
with pytest.raises(exceptions.ConfigError) as e:
@@ -306,12 +350,14 @@ def test_objects_keymap():
bindings = {
'refresh': ['a', 0x12, '<LF>', '<KEY_UP>'],
'exit': [],
Command('UPVOTE'): ['b', '<KEY_F5>']
Command('UPVOTE'): ['b', '<KEY_F5>'],
Command('PAGE_TOP'): ['gg']
}
keymap = KeyMap(bindings)
assert keymap.get(Command('REFRESH')) == ['a', 0x12, '<LF>', '<KEY_UP>']
assert keymap.get(Command('exit')) == []
assert keymap.get(Command('PAGE_TOP')) == ['gg']
assert keymap.get('upvote') == ['b', '<KEY_F5>']
with pytest.raises(exceptions.ConfigError) as e:
keymap.get('downvote')
@@ -331,7 +377,8 @@ def test_objects_keymap():
assert KeyMap.parse('<LF>') == 10
assert KeyMap.parse('<KEY_UP>') == 259
assert KeyMap.parse('<KEY_F5>') == 269
for key in ('', None, '<lf>', '<DNS>', '<KEY_UD>', ''):
assert KeyMap.parse('gg') == (103, 103)
for key in ('', None, '<lf>', '<DNS>', '<KEY_UD>', '', 'ggg'):
with pytest.raises(exceptions.ConfigError) as e:
keymap.parse(key)
assert six.text_type(key) in six.text_type(e)

View File

@@ -95,6 +95,16 @@ def test_submission_open(submission_page, terminal):
assert terminal.open_browser.called
def test_submission_move_top_bottom(submission_page):
submission_page.controller.trigger('G')
assert submission_page.nav.absolute_index == 44
submission_page.controller.trigger('g')
submission_page.controller.trigger('g')
assert submission_page.nav.absolute_index == -1
def test_submission_pager(submission_page, terminal):
# View a submission with the pager