diff --git a/jedi/api_classes.py b/jedi/api_classes.py index 842fe195..77d4a489 100644 --- a/jedi/api_classes.py +++ b/jedi/api_classes.py @@ -3,6 +3,7 @@ The :mod:`api_classes` module contains the return classes of the API. These classes are the much bigger part of the whole API, because they contain the interesting information about completion and goto operations. """ +from __future__ import with_statement import re import os @@ -11,6 +12,7 @@ import functools from jedi._compatibility import unicode, next from jedi import settings +from jedi import common from jedi import parsing_representation as pr from jedi import cache import keywords @@ -144,10 +146,8 @@ class BaseDefinition(object): if not isinstance(self.definition, keywords.Keyword): par = self.definition while par is not None: - try: + with common.ignored(AttributeError): path.insert(0, par.name) - except AttributeError: - pass par = par.parent return path @@ -295,10 +295,8 @@ class BaseDefinition(object): if not path: return None # for keywords the path is empty - try: + with common.ignored(KeyError): path[0] = self._mapping[path[0]] - except KeyError: - pass for key, repl in self._tuple_mapping.items(): if tuple(path[:len(key)]) == key: path = [repl] + path[len(key):] diff --git a/jedi/cache.py b/jedi/cache.py index 5d76f24e..e2c0c236 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -30,6 +30,7 @@ import shutil from jedi._compatibility import json from jedi import settings +from jedi import common from jedi import debug # memoize caches will be deleted after every action @@ -148,12 +149,10 @@ def cache_function_definition(stmt): def cache_star_import(func): def wrapper(scope, *args, **kwargs): - try: + with common.ignored(KeyError): mods = star_import_cache[scope] if mods[0] + settings.star_import_cache_validity > time.time(): return mods[1] - except KeyError: - pass # cache is too old and therefore invalid or not available invalidate_star_import_cache(scope) mods = func(scope, *args, **kwargs) @@ -165,15 +164,13 @@ def cache_star_import(func): def invalidate_star_import_cache(module, only_main=False): """ Important if some new modules are being reparsed """ - try: + with common.ignored(KeyError): t, mods = star_import_cache[module] del star_import_cache[module] for m in mods: invalidate_star_import_cache(m, only_main=True) - except KeyError: - pass if not only_main: # We need a list here because otherwise the list is being changed diff --git a/jedi/common.py b/jedi/common.py index 08eadd6b..ebd1f25d 100644 --- a/jedi/common.py +++ b/jedi/common.py @@ -163,3 +163,13 @@ def indent_block(text, indention=' '): text = text[:-1] lines = text.split('\n') return '\n'.join(map(lambda s: indention + s, lines)) + temp + + +@contextlib.contextmanager +def ignored(*exceptions): + """Context manager that ignores all of the specified exceptions. This will + be in the standard library starting with Python 3.4.""" + try: + yield + except exceptions: + pass diff --git a/jedi/dynamic.py b/jedi/dynamic.py index 8cc0b3eb..bd87eeb2 100644 --- a/jedi/dynamic.py +++ b/jedi/dynamic.py @@ -59,6 +59,7 @@ from jedi import cache from jedi import parsing_representation as pr from jedi import modules from jedi import settings +from jedi import common from jedi import debug from jedi import fast_parser import api_classes @@ -487,10 +488,8 @@ def related_name_add_import_modules(definitions, search_name): for d in definitions: if isinstance(d.parent, pr.Import): s = imports.ImportPath(d.parent, direct_resolve=True) - try: + with common.ignored(IndexError): new.add(s.follow(is_goto=True)[0]) - except IndexError: - pass return set(definitions) | new diff --git a/jedi/evaluate.py b/jedi/evaluate.py index c76bbb27..87d59afa 100644 --- a/jedi/evaluate.py +++ b/jedi/evaluate.py @@ -68,6 +68,7 @@ backtracking algorithm. .. todo:: nonlocal statement, needed or can be ignored? (py3k) """ +from __future__ import with_statement import sys import itertools @@ -429,11 +430,9 @@ def find_name(scope, name_str, position=None, search_global=False, if isinstance(scope, (er.Instance, er.Class)) \ and hasattr(r, 'get_descriptor_return'): # handle descriptors - try: + with common.ignored(KeyError): res_new += r.get_descriptor_return(scope) continue - except KeyError: - pass res_new.append(r) return res_new @@ -462,19 +461,15 @@ def check_getattr(inst, name_str): # str is important to lose the NamePart! module = builtin.Builtin.scope name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst) - try: + with common.ignored(KeyError): result = inst.execute_subscope_by_name('__getattr__', [name]) - except KeyError: - pass if not result: # this is a little bit special. `__getattribute__` is executed # before anything else. But: I know no use case, where this # could be practical and the jedi would return wrong types. If # you ever have something, let me know! - try: + with common.ignored(KeyError): result = inst.execute_subscope_by_name('__getattribute__', [name]) - except KeyError: - pass return result @@ -536,10 +531,8 @@ def assign_tuples(tup, results, seek_name): debug.warning("invalid tuple lookup %s of result %s in %s" % (tup, results, seek_name)) else: - try: + with common.ignored(IndexError): types += func(index) - except IndexError: - pass return types result = [] @@ -648,11 +641,9 @@ def follow_call_list(call_list, follow_array=False): call = next(calls_iterator) except StopIteration: break - try: + with common.ignored(AttributeError): if str(call.name) == 'else': break - except AttributeError: - pass continue result += follow_call(call) elif call == '*': diff --git a/jedi/evaluate_representation.py b/jedi/evaluate_representation.py index 3b5c972f..d5402e4b 100644 --- a/jedi/evaluate_representation.py +++ b/jedi/evaluate_representation.py @@ -9,6 +9,8 @@ instantiated. This class represents these cases. So, why is there also a ``Class`` class here? Well, there are decorators and they change classes in Python 3. """ +from __future__ import with_statement + import copy import itertools @@ -61,10 +63,8 @@ class Instance(use_metaclass(cache.CachedMetaClass, Executable)): else: # need to execute the __init__ function, because the dynamic param # searching needs it. - try: + with common.ignored(KeyError): self.execute_subscope_by_name('__init__', self.var_args) - except KeyError: - pass # Generated instances are classes that are just generated by self # (No var_args) used. self.is_generated = False @@ -804,10 +804,8 @@ class Array(use_metaclass(cache.CachedMetaClass, pr.Base)): if isinstance(index, Instance) \ and str(index.name) in ['int', 'str'] \ and len(index.var_args) == 1: - try: + with common.ignored(KeyError, IndexError): return self.get_exact_index_types(index.var_args[0]) - except (KeyError, IndexError): - pass result = list(self._follow_values(self._array.values)) result += dynamic.check_array_additions(self) diff --git a/jedi/helpers.py b/jedi/helpers.py index c4532961..a61027c5 100644 --- a/jedi/helpers.py +++ b/jedi/helpers.py @@ -1,5 +1,8 @@ +from __future__ import with_statement + import copy +from jedi import common from jedi import parsing_representation as pr @@ -21,13 +24,11 @@ def fast_parent_copy(obj): before = () for cls in new_obj.__class__.__mro__: - try: + with common.ignored(AttributeError): if before == cls.__slots__: continue before = cls.__slots__ items += [(n, getattr(new_obj, n)) for n in before] - except AttributeError: - pass for key, value in items: # replace parent (first try _parent and then parent) @@ -35,10 +36,8 @@ def fast_parent_copy(obj): if key == 'parent' and '_parent' in items: # parent can be a property continue - try: + with common.ignored(KeyError): setattr(new_obj, key, new_elements[value]) - except KeyError: - pass elif key in ['parent_function', 'use_as_parent', '_sub_module']: continue elif isinstance(value, list): diff --git a/jedi/imports.py b/jedi/imports.py index db72925f..d6081386 100644 --- a/jedi/imports.py +++ b/jedi/imports.py @@ -11,7 +11,6 @@ correct implementation is delegated to _compatibility. This module also supports import autocompletion, which means to complete statements like ``from datetim`` (curser at the end would return ``datetime``). """ - from __future__ import with_statement import os @@ -21,6 +20,7 @@ import itertools from jedi._compatibility import find_module from jedi import modules +from jedi import common from jedi import debug from jedi import parsing_representation as pr from jedi import cache @@ -122,11 +122,9 @@ class ImportPath(pr.Base): if self.import_stmt.relative_count: rel_path = self.get_relative_path() + '/__init__.py' - try: + with common.ignored(IOError): m = modules.Module(rel_path) names += m.parser.module.get_defined_names() - except IOError: - pass else: if on_import_stmt and isinstance(scope, pr.Module) \ and scope.path.endswith('__init__.py'): @@ -274,10 +272,8 @@ class ImportPath(pr.Base): and len(self.import_path) == 1: # follow `from . import some_variable` rel_path = self.get_relative_path() - try: + with common.ignored(ImportError): current_namespace = follow_str(rel_path, '__init__') - except ImportError: - pass if current_namespace[1]: rest = self.import_path[i:] else: diff --git a/jedi/keywords.py b/jedi/keywords.py index 5495ae32..cd591aa6 100644 --- a/jedi/keywords.py +++ b/jedi/keywords.py @@ -1,7 +1,10 @@ +from __future__ import with_statement + import pydoc import keyword from jedi._compatibility import is_py3k +from jedi import common import builtin try: @@ -63,12 +66,10 @@ def imitate_pydoc(string): # with unicode strings) string = str(string) h = pydoc.help - try: + with common.ignored(KeyError): # try to access symbols string = h.symbols[string] string, _, related = string.partition(' ') - except KeyError: - pass get_target = lambda s: h.topics.get(s, h.keywords.get(s)) while isinstance(string, str): diff --git a/jedi/modules.py b/jedi/modules.py index 9277e742..e67cc2c3 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -27,6 +27,7 @@ from jedi import parsing_representation as pr from jedi import fast_parser from jedi import debug from jedi import settings +from jedi import common class CachedModule(object): @@ -107,11 +108,9 @@ class ModuleWithCursor(Module): def parser(self): """ get the parser lazy """ if not self._parser: - try: + with common.ignored(KeyError): parser = cache.parser_cache[self.path].parser cache.invalidate_star_import_cache(parser.module) - except KeyError: - pass # Call the parser already here, because it will be used anyways. # Also, the position is here important (which will not be used by # default), therefore fill the cache here. @@ -348,10 +347,8 @@ def sys_path_with_modifications(module): return [] # support for modules without a path is intentionally bad. curdir = os.path.abspath(os.curdir) - try: + with common.ignored(OSError): os.chdir(os.path.dirname(module.path)) - except OSError: - pass result = check_module(module) result += detect_django_path(module.path) @@ -372,12 +369,10 @@ def detect_django_path(module_path): else: module_path = new - try: + with common.ignored(IOError): with open(module_path + os.path.sep + 'manage.py'): debug.dbg('Found django path: %s' % module_path) result.append(module_path) - except IOError: - pass return result diff --git a/jedi/parsing.py b/jedi/parsing.py index 69607fec..e5279f71 100644 --- a/jedi/parsing.py +++ b/jedi/parsing.py @@ -15,6 +15,7 @@ within the statement. This lowers memory usage and cpu time and reduces the complexity of the ``Parser`` (there's another parser sitting inside ``Statement``, which produces ``Array`` and ``Call``). """ +from __future__ import with_statement import tokenize import keyword @@ -396,7 +397,7 @@ class Parser(object): self._check_user_stmt(stmt) # Attribute docstring (PEP 257) support - try: + with common.ignored(IndexError, AttributeError): # If string literal is being parsed first_tok = stmt.token_list[0] if (not stmt.set_vars and @@ -405,8 +406,6 @@ class Parser(object): first_tok[0] == tokenize.STRING): # ... then set it as a docstring self.scope.statements[-1].add_docstr(first_tok[1]) - except (IndexError, AttributeError): - pass if tok in always_break + not_first_break: self._gen.push_last_back() diff --git a/jedi/parsing_representation.py b/jedi/parsing_representation.py index 311f38ab..d4b8380c 100644 --- a/jedi/parsing_representation.py +++ b/jedi/parsing_representation.py @@ -34,6 +34,7 @@ statements in this scope. Check this out: See also :attr:`Scope.subscopes` and :attr:`Scope.statements`. """ +from __future__ import with_statement import os import re @@ -1280,11 +1281,8 @@ class Array(Call): inner = [] for i, stmt in enumerate(self.values): s = '' - try: + with common.ignored(IndexError): key = self.keys[i] - except IndexError: - pass - else: s += key.get_code(new_line=False) + ': ' s += stmt.get_code(new_line=False) inner.append(s) diff --git a/jedi/refactoring.py b/jedi/refactoring.py index a724ec16..ced1a732 100644 --- a/jedi/refactoring.py +++ b/jedi/refactoring.py @@ -12,11 +12,11 @@ following functions (sometimes bug-prone): - extract variable - inline variable """ - from __future__ import with_statement import difflib +from jedi import common from jedi import modules from jedi import helpers from jedi import parsing_representation as pr @@ -168,7 +168,7 @@ def inline(script): dct = {} definitions = script.goto() - try: + with common.ignored(AssertionError): assert len(definitions) == 1 stmt = definitions[0].definition related_names = script.related_names() @@ -202,7 +202,4 @@ def inline(script): else: new_lines.pop(index) - except AssertionError: - pass - return Refactoring(dct)