Use names of classes to infer names of instances

This commit is contained in:
Dave Halter
2018-08-03 12:22:42 +02:00
parent f34a9281b9
commit bbb1502e06
2 changed files with 80 additions and 37 deletions

View File

@@ -169,7 +169,7 @@ class AbstractInstanceContext(Context):
def create_init_executions(self): def create_init_executions(self):
for name in self.get_function_slot_names(u'__init__'): for name in self.get_function_slot_names(u'__init__'):
if isinstance(name, SelfName): if isinstance(name, LazyInstanceClassName):
function = FunctionContext.from_context( function = FunctionContext.from_context(
self.parent_context, self.parent_context,
name.tree_name.parent name.tree_name.parent
@@ -351,10 +351,15 @@ class SelfName(filters.TreeNameDefinition):
return self._instance.create_instance_context(self.class_context, self.tree_name) return self._instance.create_instance_context(self.class_context, self.tree_name)
class LazyInstanceClassName(SelfName): class LazyInstanceClassName(object):
def __init__(self, instance, class_context, class_member_name):
self._instance = instance
self.class_context = class_context
self._class_member_name = class_member_name
@iterator_to_context_set @iterator_to_context_set
def infer(self): def infer(self):
for result_context in super(LazyInstanceClassName, self).infer(): for result_context in self._class_member_name.infer():
if isinstance(result_context, FunctionContext): if isinstance(result_context, FunctionContext):
# Classes are never used to resolve anything within the # Classes are never used to resolve anything within the
# functions. Only other functions and modules will resolve # functions. Only other functions and modules will resolve
@@ -364,12 +369,45 @@ class LazyInstanceClassName(SelfName):
for c in apply_py__get__(result_context, self._instance): for c in apply_py__get__(result_context, self._instance):
yield c yield c
def __getattr__(self, name):
return getattr(self._class_member_name, name)
class InstanceClassFilter(filters.ParserTreeFilter):
class InstanceClassFilter(filters.AbstractFilter):
"""
This filter is special in that it uses the class filter and wraps the
resulting names in LazyINstanceClassName. The idea is that the class name
filtering can be very flexible and always be reflected in instances.
"""
name_class = LazyInstanceClassName name_class = LazyInstanceClassName
def __init__(self, evaluator, context, class_context, origin_scope): def __init__(self, evaluator, context, class_context, origin_scope):
super(InstanceClassFilter, self).__init__( self._instance = context
self._class_context = class_context
self._class_filter = next(class_context.get_filters(
search_global=False,
origin_scope=origin_scope,
is_instance=True,
))
def get(self, name):
return self._convert(self._class_filter.get(name))
def values(self):
return self._convert(self._class_filter.values())
def _convert(self, names):
return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names]
class SelfAttributeFilter(filters.ParserTreeFilter):
"""
This class basically filters all the use cases where `self.*` was assigned.
"""
name_class = SelfName
def __init__(self, evaluator, context, class_context, origin_scope):
super(SelfAttributeFilter, self).__init__(
evaluator=evaluator, evaluator=evaluator,
context=context, context=context,
node_context=class_context, node_context=class_context,
@@ -377,32 +415,6 @@ class InstanceClassFilter(filters.ParserTreeFilter):
) )
self._class_context = class_context self._class_context = class_context
def _equals_origin_scope(self):
node = self._origin_scope
while node is not None:
if node == self._parser_scope or node == self.context:
return True
node = get_parent_scope(node)
return False
def _access_possible(self, name):
return not name.value.startswith('__') or name.value.endswith('__') \
or self._equals_origin_scope()
def _filter(self, names):
names = super(InstanceClassFilter, self)._filter(names)
return [name for name in names if self._access_possible(name)]
def _convert_names(self, names):
return [self.name_class(self.context, self._class_context, name) for name in names]
class SelfAttributeFilter(InstanceClassFilter):
"""
This class basically filters all the use cases where `self.*` was assigned.
"""
name_class = SelfName
def _filter(self, names): def _filter(self, names):
names = self._filter_self_names(names) names = self._filter_self_names(names)
if isinstance(self._parser_scope, compiled.CompiledObject) and False: if isinstance(self._parser_scope, compiled.CompiledObject) and False:
@@ -421,6 +433,21 @@ class SelfAttributeFilter(InstanceClassFilter):
if name.is_definition() and self._access_possible(name): if name.is_definition() and self._access_possible(name):
yield name yield name
def _equals_origin_scope(self):
node = self._origin_scope
while node is not None:
if node == self._parser_scope or node == self.context:
return True
node = get_parent_scope(node)
return False
def _access_possible(self, name):
return not name.value.startswith('__') or name.value.endswith('__') \
or self._equals_origin_scope()
def _convert_names(self, names):
return [self.name_class(self.context, self._class_context, name) for name in names]
def _check_flows(self, names): def _check_flows(self, names):
return names return names

View File

@@ -59,9 +59,10 @@ def apply_py__get__(context, base_context):
class ClassName(TreeNameDefinition): class ClassName(TreeNameDefinition):
def __init__(self, parent_context, tree_name, name_context): def __init__(self, parent_context, tree_name, name_context, apply_decorators):
super(ClassName, self).__init__(parent_context, tree_name) super(ClassName, self).__init__(parent_context, tree_name)
self._name_context = name_context self._name_context = name_context
self._apply_decorators = apply_decorators
@iterator_to_context_set @iterator_to_context_set
def infer(self): def infer(self):
@@ -73,16 +74,29 @@ class ClassName(TreeNameDefinition):
self.parent_context.evaluator, self._name_context, self.tree_name) self.parent_context.evaluator, self._name_context, self.tree_name)
for result_context in inferred: for result_context in inferred:
for c in apply_py__get__(result_context, self.parent_context): if self._apply_decorators:
yield c for c in apply_py__get__(result_context, self.parent_context):
yield c
else:
yield result_context
class ClassFilter(ParserTreeFilter): class ClassFilter(ParserTreeFilter):
name_class = ClassName name_class = ClassName
def __init__(self, *args, **kwargs):
self._is_instance = kwargs.pop('is_instance')
super(ClassFilter, self).__init__(*args, **kwargs)
def _convert_names(self, names): def _convert_names(self, names):
return [self.name_class(self.context, name, self._node_context) return [
for name in names] self.name_class(
parent_context=self.context,
tree_name=name,
name_context=self._node_context,
apply_decorators=not self._is_instance,
) for name in names
]
def _equals_origin_scope(self): def _equals_origin_scope(self):
node = self._origin_scope node = self._origin_scope
@@ -182,7 +196,9 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)):
else: else:
yield ClassFilter( yield ClassFilter(
self.evaluator, self, node_context=cls, self.evaluator, self, node_context=cls,
origin_scope=origin_scope) origin_scope=origin_scope,
is_instance=is_instance
)
def is_class(self): def is_class(self):
return True return True