creeper-adventure/.venv/lib/python3.8/site-packages/rope/base/pyscopes.py
2022-03-31 20:20:07 -05:00

381 lines
11 KiB
Python

import rope.base.builtins
import rope.base.codeanalyze
import rope.base.pynames
from rope.base import ast, exceptions, utils
from rope.refactor import patchedast
class Scope(object):
def __init__(self, pycore, pyobject, parent_scope):
self.pycore = pycore
self.pyobject = pyobject
self.parent = parent_scope
def get_names(self):
"""Return the names defined or imported in this scope"""
return self.pyobject.get_attributes()
def get_defined_names(self):
"""Return the names defined in this scope"""
return self.pyobject._get_structural_attributes()
def get_name(self, name):
"""Return name `PyName` defined in this scope"""
if name not in self.get_names():
raise exceptions.NameNotFoundError("name %s not found" % name)
return self.get_names()[name]
def __getitem__(self, key):
"""The same as ``get_name(key)``"""
return self.get_name(key)
def __contains__(self, key):
"""The same as ``key in self.get_names()``"""
return key in self.get_names()
@utils.saveit
def get_scopes(self):
"""Return the subscopes of this scope
The returned scopes should be sorted by the order they appear.
"""
return self._create_scopes()
def lookup(self, name):
if name in self.get_names():
return self.get_names()[name]
if self.parent is not None:
return self.parent._propagated_lookup(name)
return None
def get_propagated_names(self):
"""Return the visible names of this scope
Return the names defined in this scope that are visible from
scopes containing this scope. This method returns the same
dictionary returned by `get_names()` except for `ClassScope`
which returns an empty dict.
"""
return self.get_names()
def _propagated_lookup(self, name):
if name in self.get_propagated_names():
return self.get_propagated_names()[name]
if self.parent is not None:
return self.parent._propagated_lookup(name)
return None
def _create_scopes(self):
return [
pydefined.get_scope() for pydefined in self.pyobject._get_defined_objects()
]
def _get_global_scope(self):
current = self
while current.parent is not None:
current = current.parent
return current
def get_start(self):
return self.pyobject.get_ast().lineno
def get_body_start(self):
body = self.pyobject.get_ast().body
if body:
return body[0].lineno
return self.get_start()
def get_end(self):
pymodule = self._get_global_scope().pyobject
return pymodule.logical_lines.logical_line_in(self.logical_end)[1]
@utils.saveit
def get_logical_end(self):
global_scope = self._get_global_scope()
return global_scope._scope_finder.find_scope_end(self)
start = property(get_start)
end = property(get_end)
logical_end = property(get_logical_end)
def get_kind(self):
pass
def get_region(self):
self._calculate_scope_regions_for_module()
node = self.pyobject.get_ast()
region = patchedast.node_region(node)
return region
def _calculate_scope_regions_for_module(self):
self._get_global_scope()._calculate_scope_regions()
def in_region(self, offset):
"""Checks if offset is in scope region"""
region = self.get_region()
return region[0] < offset < region[1]
class GlobalScope(Scope):
def __init__(self, pycore, module):
super(GlobalScope, self).__init__(pycore, module, None)
self.names = module._get_concluded_data()
def get_start(self):
return 1
def get_kind(self):
return "Module"
def get_name(self, name):
try:
return self.pyobject[name]
except exceptions.AttributeNotFoundError:
if name in self.builtin_names:
return self.builtin_names[name]
raise exceptions.NameNotFoundError("name %s not found" % name)
@utils.saveit
def _calculate_scope_regions(self):
source = self._get_source()
patchedast.patch_ast(self.pyobject.get_ast(), source)
def _get_source(self):
return self.pyobject.source_code
def get_names(self):
if self.names.get() is None:
result = dict(self.builtin_names)
result.update(super(GlobalScope, self).get_names())
self.names.set(result)
return self.names.get()
def get_inner_scope_for_line(self, lineno, indents=None):
return self._scope_finder.get_holding_scope(self, lineno, indents)
def get_inner_scope_for_offset(self, offset):
return self._scope_finder.get_holding_scope_for_offset(self, offset)
@property
@utils.saveit
def _scope_finder(self):
return _HoldingScopeFinder(self.pyobject)
@property
def builtin_names(self):
return rope.base.builtins.builtins.get_attributes()
class ComprehensionScope(Scope):
def __init__(self, pycore, pyobject, visitor):
super(ComprehensionScope, self).__init__(
pycore, pyobject, pyobject.parent.get_scope()
)
self.names = None
self.returned_asts = None
self.defineds = None
self.visitor = visitor
def _get_names(self):
if self.names is None:
self._visit_comprehension()
return self.names
def get_names(self):
return self._get_names()
def _visit_comprehension(self):
if self.names is None:
new_visitor = self.visitor(self.pycore, self.pyobject)
for node in ast.get_child_nodes(self.pyobject.get_ast()):
ast.walk(node, new_visitor)
self.names = dict(self.parent.get_names())
self.names.update(new_visitor.names)
self.defineds = new_visitor.defineds
def get_logical_end(self):
return self.get_start()
logical_end = property(get_logical_end)
def get_body_start(self):
return self.get_start()
class FunctionScope(Scope):
def __init__(self, pycore, pyobject, visitor):
super(FunctionScope, self).__init__(
pycore, pyobject, pyobject.parent.get_scope()
)
self.names = None
self.returned_asts = None
self.is_generator = None
self.defineds = None
self.visitor = visitor
def _get_names(self):
if self.names is None:
self._visit_function()
return self.names
def _visit_function(self):
if self.names is None:
new_visitor = self.visitor(self.pycore, self.pyobject)
for n in ast.get_child_nodes(self.pyobject.get_ast()):
ast.walk(n, new_visitor)
self.names = new_visitor.names
self.names.update(self.pyobject.get_parameters())
self.returned_asts = new_visitor.returned_asts
self.is_generator = new_visitor.generator
self.defineds = new_visitor.defineds
def _get_returned_asts(self):
if self.names is None:
self._visit_function()
return self.returned_asts
def _is_generator(self):
if self.is_generator is None:
self._get_returned_asts()
return self.is_generator
def get_names(self):
return self._get_names()
def _create_scopes(self):
if self.defineds is None:
self._visit_function()
return [pydefined.get_scope() for pydefined in self.defineds]
def get_kind(self):
return "Function"
def invalidate_data(self):
for pyname in self.get_names().values():
if isinstance(
pyname,
(rope.base.pynames.AssignedName, rope.base.pynames.EvaluatedName),
):
pyname.invalidate()
class ClassScope(Scope):
def __init__(self, pycore, pyobject):
super(ClassScope, self).__init__(pycore, pyobject, pyobject.parent.get_scope())
def get_kind(self):
return "Class"
def get_propagated_names(self):
return {}
class _HoldingScopeFinder(object):
def __init__(self, pymodule):
self.pymodule = pymodule
def get_indents(self, lineno):
return rope.base.codeanalyze.count_line_indents(self.lines.get_line(lineno))
def _get_scope_indents(self, scope):
return self.get_indents(scope.get_start())
def get_holding_scope(self, module_scope, lineno, line_indents=None):
if line_indents is None:
line_indents = self.get_indents(lineno)
current_scope = module_scope
new_scope = current_scope
while new_scope is not None and (
new_scope.get_kind() == "Module"
or self._get_scope_indents(new_scope) <= line_indents
):
current_scope = new_scope
if (
current_scope.get_start() == lineno
and current_scope.get_kind() != "Module"
):
return current_scope
new_scope = None
for scope in current_scope.get_scopes():
if scope.get_start() <= lineno:
if lineno <= scope.get_end():
new_scope = scope
break
else:
break
return current_scope
def _is_empty_line(self, lineno):
line = self.lines.get_line(lineno)
return line.strip() == "" or line.lstrip().startswith("#")
def _get_body_indents(self, scope):
return self.get_indents(scope.get_body_start())
@staticmethod
def get_holding_scope_for_offset(scope, offset):
for inner_scope in scope.get_scopes():
if inner_scope.in_region(offset):
return _HoldingScopeFinder.get_holding_scope_for_offset(
inner_scope, offset
)
return scope
def find_scope_end(self, scope):
if not scope.parent:
return self.lines.length()
end = scope.pyobject.get_ast().body[-1].lineno
scope_start = self.pymodule.logical_lines.logical_line_in(scope.start)
if scope_start[1] >= end:
# handling one-liners
body_indents = self._get_scope_indents(scope) + 4
else:
body_indents = self._get_body_indents(scope)
for l in self.logical_lines.generate_starts(
min(end + 1, self.lines.length()), self.lines.length() + 1
):
if not self._is_empty_line(l):
if self.get_indents(l) < body_indents:
return end
else:
end = l
return end
@property
def lines(self):
return self.pymodule.lines
@property
def code(self):
return self.pymodule.source_code
@property
def logical_lines(self):
return self.pymodule.logical_lines
class TemporaryScope(Scope):
"""Currently used for list comprehensions and generator expressions
These scopes do not appear in the `get_scopes()` method of their
parent scopes.
"""
def __init__(self, pycore, parent_scope, names):
super(TemporaryScope, self).__init__(
pycore, parent_scope.pyobject, parent_scope
)
self.names = names
def get_names(self):
return self.names
def get_defined_names(self):
return self.names
def _create_scopes(self):
return []
def get_kind(self):
return "Temporary"