diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 3d204a01..7750c244 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -13,7 +13,6 @@ import os import warnings import sys -from jedi._compatibility import unicode from jedi.parser import load_grammar from jedi.parser import tree from jedi.parser.fast import FastParser @@ -195,7 +194,7 @@ class Script(object): if leaf is None: return [] - context = self._evaluator.create_context(self._get_module(), leaf.parent) + context = self._evaluator.create_context(self._get_module(), leaf) definitions = helpers.evaluate_goto_definition(self._evaluator, context, leaf) names = [s.name for s in definitions] @@ -328,11 +327,12 @@ class Script(object): self._evaluator.analysis_modules = [module_node] try: for node in module_node.nodes_to_execute(): + context = self._get_module().create_context(node) if node.type in ('funcdef', 'classdef'): - if node.type == 'classdef': - continue - raise NotImplementedError - er.Function(self._evaluator, node).get_decorated_func() + # TODO This is stupid, should be private + from jedi.evaluate.finder import _name_to_types + # Resolve the decorators. + _name_to_types(self._evaluator, context, node.children[1]) elif isinstance(node, tree.Import): import_names = set(node.get_defined_names()) if node.is_nested(): @@ -340,12 +340,12 @@ class Script(object): for n in import_names: imports.ImportWrapper(context, n).follow() elif node.type == 'expr_stmt': - types = self._evaluator.eval_element(node) + types = context.eval_node(node) for testlist in node.children[:-1:2]: # Iterate tuples. unpack_tuple_to_dict(self._evaluator, types, testlist) else: - try_iter_content(self._evaluator.goto_definitions(node)) + try_iter_content(self._evaluator.goto_definitions(context, node)) self._evaluator.reset_recursion_limitations() ana = [a for a in self._evaluator.analysis if self.path == a.path] diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 0380d544..b86ce09d 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -133,7 +133,7 @@ class BaseDefinition(object): >>> defs = sorted(defs, key=lambda d: d.line) >>> defs # doctest: +NORMALIZE_WHITESPACE [, , - , ] + , ] Finally, here is what you can get from :attr:`type`: @@ -489,7 +489,7 @@ class Completion(BaseDefinition): return '%s: %s%s' % (t, desc, line) def __repr__(self): - return '<%s: %s>' % (type(self).__name__, self._name) + return '<%s: %s>' % (type(self).__name__, self._name.string_name) @memoize_method def _follow_statements_imports(self): @@ -556,7 +556,7 @@ class Definition(BaseDefinition): """ typ = self.type tree_name = self._name.tree_name - if typ in ('function', 'class', 'module') or tree_name is None: + if typ in ('function', 'class', 'module', 'instance') or tree_name is None: if typ == 'function': # For the description we want a short and a pythonic way. typ = 'def' @@ -743,8 +743,8 @@ class CallSignature(Definition): return self._executable.get_parent_until() def __repr__(self): - return '<%s: %s index %s>' % (type(self).__name__, self._name, - self.index) + return '<%s: %s index %s>' % \ + (type(self).__name__, self._name.string_name, self.index) class _Param(Definition): diff --git a/jedi/api/usages.py b/jedi/api/usages.py index b404d97e..9519c0b1 100644 --- a/jedi/api/usages.py +++ b/jedi/api/usages.py @@ -9,19 +9,31 @@ def usages(evaluator, definition_names, mods): """ :param definitions: list of Name """ + def resolve_names(definition_names): + for name in definition_names: + if name.api_type == 'module': + found = False + for context in name.infer(): + found = True + yield context.name + if not found: + yield name + else: + yield name + def compare_array(definition_names): """ `definitions` are being compared by module/start_pos, because sometimes the id's of the objects change (e.g. executions). """ return [ - (d.get_root_context(), d.start_pos) - for d in definition_names + (name.get_root_context(), name.start_pos) + for name in resolve_names(definition_names) ] search_name = list(definition_names)[0].string_name compare_definitions = compare_array(definition_names) mods = mods | set([d.get_root_context() for d in definition_names]) - definition_names = set(definition_names) + definition_names = set(resolve_names(definition_names)) for m in imports.get_modules_containing_name(evaluator, mods, search_name): if isinstance(m, ModuleContext): for name_node in m.module_node.used_names.get(search_name, []): diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 80a6eade..3e251319 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -169,10 +169,10 @@ class Evaluator(object): dct = {str(for_stmt.children[1]): lazy_context.infer()} with helpers.predefine_names(context, for_stmt, dct): t = self.eval_element(context, rhs) - left = precedence.calculate(self, left, operator, t) + left = precedence.calculate(self, context, left, operator, t) types = left else: - types = precedence.calculate(self, left, operator, types) + types = precedence.calculate(self, context, left, operator, types) debug.dbg('eval_statement result %s', types) return types @@ -180,9 +180,16 @@ class Evaluator(object): if isinstance(context, iterable.CompForContext): return self._eval_element_not_cached(context, element) - if_stmt = element.get_parent_until((tree.IfStmt, tree.ForStmt, tree.IsScope)) + if_stmt = element + while if_stmt is not None: + if_stmt = if_stmt.parent + if if_stmt.type in ('if_stmt', 'for_stmt'): + break + if if_stmt.is_scope(): + if_stmt = None + break predefined_if_name_dict = context.predefined_names.get(if_stmt) - if predefined_if_name_dict is None and isinstance(if_stmt, tree.IfStmt): + if predefined_if_name_dict is None and if_stmt and if_stmt.type == 'if_stmt': if_stmt_test = if_stmt.children[1] name_dicts = [{}] # If we already did a check, we don't want to do it again -> If @@ -274,7 +281,7 @@ class Evaluator(object): for trailer in element.children[1:]: if trailer == '**': # has a power operation. right = self.eval_element(context, element.children[2]) - types = set(precedence.calculate(self, types, trailer, right)) + types = set(precedence.calculate(self, context, types, trailer, right)) break types = self.eval_trailer(context, types, trailer) elif element.type in ('testlist_star_expr', 'testlist',): @@ -344,7 +351,7 @@ class Evaluator(object): types = self._eval_atom(context, c[0]) for string in c[1:]: right = self._eval_atom(context, string) - types = precedence.calculate(self, types, '+', right) + types = precedence.calculate(self, context, types, '+', right) return types # Parentheses without commas are not tuples. elif c[0] == '(' and not len(c) == 2 \ @@ -491,11 +498,6 @@ class Evaluator(object): # a name it's something you can "goto" again. return [TreeNameDefinition(context, name)] elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name: - if par.type in ('funcdef', 'classdef', 'module'): - if par.type == 'funcdef': - return [context.function_context.name] - else: - return [context.name] return [TreeNameDefinition(context, name)] elif isinstance(stmt, tree.Import): module_names = imports.ImportWrapper(context, name).follow(is_goto=True) @@ -600,5 +602,9 @@ class Evaluator(object): if node_is_context and node.is_scope(): scope_node = node else: + if node.parent.type in ('funcdef', 'classdef'): + # When we're on class/function names/leafs that define the + # object itself and not its contents. + node = node.parent scope_node = parent_scope(node) return from_scope_node(scope_node) diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index dfbb6d97..8c75cf5d 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -58,8 +58,8 @@ class Error(object): return self.__unicode__() def __eq__(self, other): - return (self.path == other.path and self.name == other.name - and self._start_pos == other._start_pos) + return (self.path == other.path and self.name == other.name and + self._start_pos == other._start_pos) def __ne__(self, other): return not self.__eq__(other) @@ -77,23 +77,19 @@ class Warning(Error): pass -def add(evaluator, name, jedi_obj, message=None, typ=Error, payload=None): +def add(context, name, jedi_name, message=None, typ=Error, payload=None): return - from jedi.evaluate.iterable import MergedNodes - while isinstance(jedi_obj, MergedNodes): - if len(jedi_obj) != 1: - # TODO is this kosher? - return - jedi_obj = list(jedi_obj)[0] - + from jedi.evaluate import Evaluator + if isinstance(context, Evaluator): + raise 1 exception = CODES[name][1] - if _check_for_exception_catch(evaluator, jedi_obj, exception, payload): + if _check_for_exception_catch(context, jedi_name, exception, payload): return - module_path = jedi_obj.get_parent_until().path - instance = typ(name, module_path, jedi_obj.start_pos, message) + module_path = jedi_name.get_root_node().path + instance = typ(name, module_path, jedi_name.start_pos, message) debug.warning(str(instance), format=False) - evaluator.analysis.append(instance) + context.evaluator.analysis.append(instance) def _check_for_setattr(instance): @@ -114,25 +110,29 @@ def _check_for_setattr(instance): for stmt in stmts) -def add_attribute_error(evaluator, scope, name): - message = ('AttributeError: %s has no attribute %s.' % (scope, name)) - from jedi.evaluate.instance import AbstractInstanceContext +def add_attribute_error(context, name): + message = ('AttributeError: %s has no attribute %s.' % (context, name)) + from jedi.evaluate.instance import AbstractInstanceContext, CompiledInstanceName # Check for __getattr__/__getattribute__ existance and issue a warning # instead of an error, if that happens. - if isinstance(scope, AbstractInstanceContext): - typ = Warning - if not (scope.get_function_slot_names('__getattr__') or - scope.get_function_slot_names('__getattribute__')): - if not _check_for_setattr(scope): - typ = Error - else: - typ = Error + typ = Error + if isinstance(context, AbstractInstanceContext): + slot_names = context.get_function_slot_names('__getattr__') + \ + context.get_function_slot_names('__getattribute__') + for n in slot_names: + if isinstance(name, CompiledInstanceName) and \ + n.parent_context.obj == object: + typ = Warning + break - payload = scope, name - add(evaluator, 'attribute-error', name, message, typ, payload) + if _check_for_setattr(context): + typ = Warning + + payload = context, name + add(context, 'attribute-error', name, message, typ, payload) -def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None): +def _check_for_exception_catch(context, jedi_name, exception, payload=None): """ Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and doesn't count as an error (if equal to `exception`). @@ -153,17 +153,18 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None): colon = next(iterator) suite = next(iterator) if branch_type == 'try' \ - and not (branch_type.start_pos < jedi_obj.start_pos <= suite.end_pos): + and not (branch_type.start_pos < jedi_name.start_pos <= suite.end_pos): return False for node in obj.except_clauses(): if node is None: return True # An exception block that catches everything. else: - except_classes = evaluator.eval_element(node) + except_classes = context.eval_node(node) for cls in except_classes: from jedi.evaluate import iterable - if isinstance(cls, iterable.Array) and cls.type == 'tuple': + if isinstance(cls, iterable.AbstractSequence) and \ + cls.array_type == 'tuple': # multiple exceptions for typ in unite(cls.py__iter__()): if check_match(typ, exception): @@ -174,7 +175,7 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None): def check_hasattr(node, suite): try: - assert suite.start_pos <= jedi_obj.start_pos < suite.end_pos + assert suite.start_pos <= jedi_name.start_pos < suite.end_pos assert node.type in ('power', 'atom_expr') base = node.children[0] assert base.type == 'name' and base.value == 'hasattr' @@ -183,28 +184,28 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None): arglist = trailer.children[1] assert arglist.type == 'arglist' from jedi.evaluate.param import Arguments - args = list(Arguments(evaluator, arglist).unpack()) + args = list(Arguments(context, arglist).unpack()) # Arguments should be very simple assert len(args) == 2 # Check name key, values = args[1] assert len(values) == 1 - names = list(evaluator.eval_element(values[0])) + names = list(context.eval_node(values[0])) assert len(names) == 1 and isinstance(names[0], CompiledObject) assert names[0].obj == str(payload[1]) # Check objects key, values = args[0] assert len(values) == 1 - objects = evaluator.eval_element(values[0]) + objects = context.eval_node(values[0]) return payload[0] in objects except AssertionError: return False - obj = jedi_obj - while obj is not None and not obj.isinstance(tree.Function, tree.Class): - if obj.isinstance(tree.Flow): + obj = jedi_name + while obj is not None and not isinstance(obj, (tree.Function, tree.Class)): + if isinstance(obj, tree.Flow): # try/except catch check if obj.isinstance(tree.TryStmt) and check_try_for_except(obj, exception): return True diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 2fbe7f48..18987de6 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -14,9 +14,7 @@ would check whether a flow has the form of ``if isinstance(a, type_or_tuple)``. Unfortunately every other thing is being ignored (e.g. a == '' would be easy to check for -> a is a string). There's big potential in these checks. """ -from itertools import chain -from jedi._compatibility import unicode from jedi.parser import tree from jedi import debug from jedi.common import unite @@ -33,9 +31,7 @@ from jedi.evaluate import analysis from jedi.evaluate import flow_analysis from jedi.evaluate import param from jedi.evaluate import helpers -from jedi.evaluate.context import TreeContext -from jedi.evaluate.cache import memoize_default -from jedi.evaluate.filters import get_global_filters, ContextName +from jedi.evaluate.filters import get_global_filters, TreeNameDefinition def filter_after_position(names, position, origin=None): @@ -128,22 +124,22 @@ class NameFinder(object): isinstance(self._name.parent.parent, tree.Param)): if isinstance(self._name, tree.Name): if attribute_lookup: - analysis.add_attribute_error(self._evaluator, - self._context, self._name) + analysis.add_attribute_error(self._context, self._name) else: message = ("NameError: name '%s' is not defined." % self._string_name) - analysis.add(self._evaluator, 'name-error', self._name, - message) + analysis.add(self._context, 'name-error', self._name, message) return types - def get_filters(self, search_global=False): + def _get_origin_scope(self): if isinstance(self._name, tree.Name): - origin_scope = self._name.get_parent_until(tree.Scope, reverse=True) + return self._name.get_parent_until(tree.Scope, reverse=True) else: - origin_scope = None + return None + def get_filters(self, search_global=False): + origin_scope = self._get_origin_scope() if search_global: return get_global_filters(self._evaluator, self._context, self._position, origin_scope) else: @@ -321,15 +317,17 @@ class NameFinder(object): # Add isinstance and other if/assert knowledge. if not types and isinstance(self._name, tree.Name) and \ not isinstance(self._name_context, AbstractInstanceContext): - # Ignore FunctionExecution parents for now. flow_scope = self._name + base_node = self._name_context.get_node() + if base_node.type == 'comp_for': + return types while True: flow_scope = flow_scope.get_parent_scope(include_flows=True) n = _check_flow_information(self._name_context, flow_scope, self._name, self._position) if n is not None: return n - if flow_scope == self._name_context.get_node(): + if flow_scope == base_node: break return types diff --git a/jedi/evaluate/flow_analysis.py b/jedi/evaluate/flow_analysis.py index 2bdd5ab5..a20843db 100644 --- a/jedi/evaluate/flow_analysis.py +++ b/jedi/evaluate/flow_analysis.py @@ -1,6 +1,3 @@ -from jedi.parser import tree - - class Status(object): lookup_table = {} diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index d7c8efa7..a4798d90 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -147,10 +147,10 @@ class NestedImportModule(tree.Module): self._nested_import) -def _add_error(evaluator, name, message=None): +def _add_error(context, name, message=None): + # Should be a name, not a string! if hasattr(name, 'parent'): - # Should be a name, not a string! - analysis.add(evaluator, 'import-error', name, message) + analysis.add(context, 'import-error', name, message) def get_init_path(directory_path): @@ -168,25 +168,21 @@ def get_init_path(directory_path): class ImportName(AbstractNameDefinition): start_pos = (1, 0) - def __init__(self, parent_module, string_name): - self.parent_module = parent_module + def __init__(self, parent_context, string_name): + self.parent_context = parent_context self.string_name = string_name def infer(self): return Importer( - self.parent_module.evaluator, + self.parent_context.evaluator, [self.string_name], - self.parent_module, + self.parent_context, ).follow() def get_root_context(self): # Not sure if this is correct. return self.parent_context.get_root_context() - @property - def parent_context(self): - return self.parent_module - @property def api_type(self): return 'module' @@ -195,18 +191,12 @@ class ImportName(AbstractNameDefinition): class SubModuleName(ImportName): def infer(self): return Importer( - self.parent_module.evaluator, + self.parent_context.evaluator, [self.string_name], - self.parent_module, + self.parent_context, level=1 ).follow() - @property - def parent_context(self): - # This is a bit of a special case. But it seems like it's working well. - # Since a SubModuleName is basically a lazy name to a module - return next(iter(self.infer())) - class Importer(object): def __init__(self, evaluator, import_path, module_context, level=0): @@ -249,7 +239,7 @@ class Importer(object): if dir_name: import_path.insert(0, dir_name) else: - _add_error(self._evaluator, import_path[-1]) + _add_error(module_context, import_path[-1]) import_path = [] # TODO add import error. debug.warning('Attempted relative import beyond top-level package.') @@ -336,7 +326,7 @@ class Importer(object): method = parent_module.py__path__ except AttributeError: # The module is not a package. - _add_error(self._evaluator, import_path[-1]) + _add_error(parent_module, import_path[-1]) return set() else: paths = method() @@ -351,7 +341,7 @@ class Importer(object): except ImportError: module_path = None if module_path is None: - _add_error(self._evaluator, import_path[-1]) + _add_error(parent_module, import_path[-1]) return set() else: parent_module = None @@ -367,7 +357,7 @@ class Importer(object): sys.path = temp except ImportError: # The module is not a package. - _add_error(self._evaluator, import_path[-1]) + _add_error(parent_module, import_path[-1]) return set() source = None diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 5534a4a2..347895e3 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -63,11 +63,9 @@ class AbstractArguments(): Evaluates all arguments as a support for static analysis (normally Jedi). """ - raise DeprecationWarning - for key, element_values in self.unpack(): - for element in element_values: - types = self._evaluator.eval_element(self.context, element) - try_iter_content(types) + for key, lazy_context in self.unpack(): + types = lazy_context.infer() + try_iter_content(types) class TreeArguments(AbstractArguments): @@ -260,7 +258,7 @@ def get_params(evaluator, parent_context, func, var_args): % (func.name, key)) calling_va = _get_calling_var_args(evaluator, var_args) if calling_va is not None: - analysis.add(evaluator, 'type-error-multiple-values', + analysis.add(parent_context, 'type-error-multiple-values', calling_va, message=m) else: keys_used[key] = ExecutedParam(parent_context, key_param, var_args, argument) @@ -302,7 +300,7 @@ def get_params(evaluator, parent_context, func, var_args): calling_va = var_args.get_calling_var_args() if calling_va is not None: m = _error_argument_count(func, len(unpacked_va)) - analysis.add(evaluator, 'type-error-too-few-arguments', + analysis.add(parent_context, 'type-error-too-few-arguments', calling_va, message=m) else: result_arg = argument @@ -323,13 +321,13 @@ def get_params(evaluator, parent_context, func, var_args): calling_va = _get_calling_var_args(evaluator, var_args) if calling_va is not None: m = _error_argument_count(func, len(unpacked_va)) - analysis.add(evaluator, 'type-error-too-few-arguments', + analysis.add(parent_context, 'type-error-too-few-arguments', calling_va, message=m) for key, argument in non_matching_keys.items(): m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (func.name, key) - analysis.add(evaluator, 'type-error-keyword-argument', argument.whatever, message=m) + analysis.add(parent_context, 'type-error-keyword-argument', argument.whatever, message=m) remaining_arguments = list(var_arg_iterator) if remaining_arguments: @@ -354,7 +352,7 @@ def get_params(evaluator, parent_context, func, var_args): # print('\t\tnonkw', non_kw_param.parent.var_args.argument_node, ) if origin_args not in [f.parent.parent for f in first_values]: continue - analysis.add(evaluator, 'type-error-too-many-arguments', + analysis.add(parent_context, 'type-error-too-many-arguments', v, message=m) return result_params diff --git a/jedi/evaluate/precedence.py b/jedi/evaluate/precedence.py index ae8e274f..51c5c94c 100644 --- a/jedi/evaluate/precedence.py +++ b/jedi/evaluate/precedence.py @@ -59,13 +59,13 @@ def calculate_children(evaluator, context, children): types = context.eval_node(right) # Otherwise continue, because of uncertainty. else: - types = calculate(evaluator, types, operator, + types = calculate(evaluator, context, types, operator, context.eval_node(right)) debug.dbg('calculate_children types %s', types) return types -def calculate(evaluator, left_result, operator, right_result): +def calculate(evaluator, context, left_result, operator, right_result): result = set() if not left_result or not right_result: # illegal slices e.g. cause left/right_result to be None @@ -80,7 +80,7 @@ def calculate(evaluator, left_result, operator, right_result): else: for left in left_result: for right in right_result: - result |= _element_calculate(evaluator, left, operator, right) + result |= _element_calculate(evaluator, context, left, operator, right) return result @@ -125,7 +125,7 @@ def _is_list(obj): return isinstance(obj, iterable.AbstractSequence) and obj.array_type == 'list' -def _element_calculate(evaluator, left, operator, right): +def _element_calculate(evaluator, context, left, operator, right): from jedi.evaluate import iterable, instance l_is_num = _is_number(left) r_is_num = _is_number(right) @@ -173,7 +173,7 @@ def _element_calculate(evaluator, left, operator, right): if operator in ('+', '-') and l_is_num != r_is_num \ and not (check(left) or check(right)): message = "TypeError: unsupported operand type(s) for +: %s and %s" - analysis.add(evaluator, 'type-error-operation', operator, + analysis.add(context, 'type-error-operation', operator, message % (left, right)) return set([left, right]) diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index cfe8dee9..6617049d 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -58,11 +58,11 @@ def execute(evaluator, obj, arguments): def _follow_param(evaluator, arguments, index): try: - key, values = list(arguments.unpack())[index] + key, lazy_context = list(arguments.unpack())[index] except IndexError: return set() else: - return unite(evaluator.eval_element(v) for v in values) + return lazy_context.infer() def argument_clinic(string, want_obj=False, want_context=False, want_arguments=False): @@ -219,7 +219,7 @@ def builtins_isinstance(evaluator, objects, types, arguments): message = 'TypeError: isinstance() arg 2 must be a ' \ 'class, type, or tuple of classes and types, ' \ 'not %s.' % cls_or_tup - analysis.add(evaluator, 'type-error-isinstance', node, message) + analysis.add(cls_or_tup, 'type-error-isinstance', node, message) return set(compiled.create(evaluator, x) for x in bool_results) @@ -244,11 +244,12 @@ def collections_namedtuple(evaluator, obj, arguments): _fields = list(_follow_param(evaluator, arguments, 1))[0] if isinstance(_fields, compiled.CompiledObject): fields = _fields.obj.replace(',', ' ').split() - elif isinstance(_fields, iterable.Array): - try: - fields = [v.obj for v in unite(_fields.py__iter__())] - except AttributeError: - return set() + elif isinstance(_fields, iterable.AbstractSequence): + fields = [ + v.obj + for lazy_context in _fields.py__iter__() + for v in lazy_context.infer() if hasattr(v, 'obj') + ] else: return set() @@ -265,7 +266,7 @@ def collections_namedtuple(evaluator, obj, arguments): # Parse source generated_class = ParserWithRecovery(evaluator.grammar, unicode(source)).module.subscopes[0] - return set([er.Class(evaluator, generated_class)]) + return set([er.ClassContext(evaluator, generated_class, evaluator.BUILTINS)]) @argument_clinic('first, /') diff --git a/test/conftest.py b/test/conftest.py index ac04e268..56193a7d 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -71,9 +71,12 @@ def pytest_generate_tests(metafunc): if 'static_analysis_case' in metafunc.fixturenames: base_dir = os.path.join(os.path.dirname(__file__), 'static_analysis') + cases = list(collect_static_analysis_tests(base_dir, test_files)) metafunc.parametrize( 'static_analysis_case', - collect_static_analysis_tests(base_dir, test_files)) + cases, + ids=[c.name for c in cases] + ) def collect_static_analysis_tests(base_dir, test_files): @@ -91,6 +94,7 @@ class StaticAnalysisCase(object): """ def __init__(self, path): self._path = path + self.name = os.path.basename(path) with open(path) as f: self._source = f.read() @@ -98,7 +102,6 @@ class StaticAnalysisCase(object): for line in self._source.splitlines(): self.skip = self.skip or run.skip_python_version(line) - def collect_comparison(self): cases = [] for line_nr, line in enumerate(self._source.splitlines(), 1):