| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c86f222 commit 6b1d291
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -74,6 +74,22 @@ | |||
| 74 | 74 | def after_last_dot(name): | |
| 75 | 75 | return name.rstrip('.').rsplit('.')[-1] | |
| 76 | 76 | ||
| 77 | + def few_enough_underscores(current, match): | ||
| 78 | + """Returns whether match should be shown based on current | ||
| 79 | + | ||
| 80 | + if current is _, True if match starts with 0 or 1 underscore | ||
| 81 | + if current is __, True regardless of match | ||
| 82 | + otherwise True if match does not start with any underscore | ||
| 83 | + """ | ||
| 84 | + if current.startswith('__'): | ||
| 85 | + return True | ||
| 86 | + elif current.startswith('_') and not match.startswith('__'): | ||
| 87 | + return True | ||
| 88 | + elif match.startswith('_'): | ||
| 89 | + return False | ||
| 90 | + else: | ||
| 91 | + return True | ||
| 92 | + | ||
| 77 | 93 | ||
| 78 | 94 | def method_match_simple(word, size, text): | |
| 79 | 95 | return word[:size] == text | |
@@ -255,18 +271,9 @@ def matches(self, cursor_offset, line, **kwargs): | |||
| 255 | 271 | matches = set(''.join([r.word[:-i], m]) | |
| 256 | 272 | for m in self.attr_matches(methodtext, locals_)) | |
| 257 | 273 | ||
| 258 | - # TODO add open paren for methods via _callable_prefix (or decide not | ||
| 259 | - # to) unless the first character is a _ filter out all attributes | ||
| 260 | - # starting with a _ | ||
| 261 | - if r.word.split('.')[-1].startswith('__'): | ||
| 262 | - pass | ||
| 263 | - elif r.word.split('.')[-1].startswith('_'): | ||
| 264 | - matches = set(match for match in matches | ||
| 265 | - if not match.split('.')[-1].startswith('__')) | ||
| 266 | - else: | ||
| 267 | - matches = set(match for match in matches | ||
| 268 | - if not match.split('.')[-1].startswith('_')) | ||
| 269 | - return matches | ||
| 274 | + return set(m for m in matches | ||
| 275 | + if few_enough_underscores(r.word.split('.')[-1], | ||
| 276 | + m.split('.')[-1])) | ||
| 270 | 277 | ||
| 271 | 278 | def locate(self, current_offset, line): | |
| 272 | 279 | return lineparts.current_dotted_attribute(current_offset, line) | |
@@ -470,14 +477,8 @@ def matches(self, cursor_offset, line, **kwargs): | |||
| 470 | 477 | # strips leading dot | |
| 471 | 478 | matches = [m[1:] for m in self.attr_lookup(obj, '', attr.word)] | |
| 472 | 479 | ||
| 473 | - if attr.word.startswith('__'): | ||
| 474 | - pass | ||
| 475 | - elif attr.word.startswith('_'): | ||
| 476 | - matches = set(match for match in matches | ||
| 477 | - if not match.startswith('__')) | ||
| 478 | - else: | ||
| 479 | - matches = set(match for match in matches | ||
| 480 | - if not match.split('.')[-1].startswith('_')) | ||
| 480 | + | ||
| 481 | + return set(m for m in matches if few_enough_underscores(attr.word, m)) | ||
| 481 | 482 | return matches | |
| 482 | 483 | ||
| 483 | 484 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -168,10 +168,12 @@ def evaluate_current_expression(cursor_offset, line, namespace=None): | |||
| 168 | 168 | """ | |
| 169 | 169 | Return evaluated expression to the right of the dot of current attribute. | |
| 170 | 170 | ||
| 171 | - build asts from with increasing numbers of characters. | ||
| 172 | - Find the biggest valid ast. | ||
| 173 | - Once our attribute access is a subtree, stop | ||
| 171 | + Only evaluates builtin objects, and do any attribute lookup. | ||
| 174 | 172 | """ | |
| 173 | + # Builds asts from with increasing numbers of characters back from cursor. | ||
| 174 | + # Find the biggest valid ast. | ||
| 175 | + # Once our attribute access is found, return its .value subtree | ||
| 176 | + | ||
| 175 | 177 | if namespace is None: | |
| 176 | 178 | namespace = {} | |
| 177 | 179 | ||
@@ -207,6 +209,7 @@ def parse_trees(cursor_offset, line): | |||
| 207 | 209 | ||
| 208 | 210 | ||
| 209 | 211 | def evaluate_current_attribute(cursor_offset, line, namespace=None): | |
| 212 | + """Safely evaluates the expression attribute lookup currently occuring on""" | ||
| 210 | 213 | # this function runs user code in case of custom descriptors, | |
| 211 | 214 | # so could fail in any way | |
| 212 | 215 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -230,6 +230,13 @@ def test_issue583(self): | |||
| 230 | 230 | self.repl.set_docstring() | |
| 231 | 231 | self.assertIsNot(self.repl.docstring, None) | |
| 232 | 232 | ||
| 233 | + def test_methods_of_expressions(self): | ||
| 234 | + self.set_input_line("'a'.capitalize(") | ||
| 235 | + self.assertTrue(self.repl.get_args()) | ||
| 236 | + | ||
| 237 | + self.set_input_line("(1 + 1).bit_length(") | ||
| 238 | + self.assertTrue(self.repl.get_args()) | ||
| 239 | + | ||
| 233 | 240 | ||
| 234 | 241 | class TestGetSource(unittest.TestCase): | |
| 235 | 242 | def setUp(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,7 @@ | |||
| 1 | 1 | # -*- coding: utf-8 -*- | |
| 2 | 2 | ||
| 3 | 3 | import ast | |
| 4 | + import numbers | ||
| 4 | 5 | ||
| 5 | 6 | from bpython.simpleeval import (simple_eval, | |
| 6 | 7 | evaluate_current_expression, | |
@@ -60,6 +61,23 @@ def __getattr__(inner_self, attr): | |||
| 60 | 61 | with self.assertRaises(ValueError): | |
| 61 | 62 | simple_eval('a[1]', {'a': SchrodingersDict()}) | |
| 62 | 63 | ||
| 64 | + def test_operators_on_suspicious_types(self): | ||
| 65 | + class Spam(numbers.Number): | ||
| 66 | + def __add__(inner_self, other): | ||
| 67 | + self.fail("doing attribute lookup might have side effects") | ||
| 68 | + | ||
| 69 | + with self.assertRaises(ValueError): | ||
| 70 | + simple_eval('a + 1', {'a': Spam()}) | ||
| 71 | + | ||
| 72 | + def test_operators_on_numbers(self): | ||
| 73 | + self.assertEqual(simple_eval('-2'), -2) | ||
| 74 | + self.assertEqual(simple_eval('1 + 1'), 2) | ||
| 75 | + self.assertEqual(simple_eval('a - 2', {'a':1}), -1) | ||
| 76 | + with self.assertRaises(ValueError): | ||
| 77 | + simple_eval('2 * 3') | ||
| 78 | + with self.assertRaises(ValueError): | ||
| 79 | + simple_eval('2 ** 3') | ||
| 80 | + | ||
| 63 | 81 | def test_function_calls_raise(self): | |
| 64 | 82 | with self.assertRaises(ValueError): | |
| 65 | 83 | simple_eval('1()') | |
| Back | FazBrowse Home | New Git URL |
0 commit comments