Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/autoimport.py
1496 views
1
import re
2
3
from rope.base import (exceptions, pynames, resourceobserver,
4
taskhandle, pyobjects, builtins, resources)
5
from rope.refactor import importutils
6
7
8
class AutoImport(object):
9
"""A class for finding the module that provides a name
10
11
This class maintains a cache of global names in python modules.
12
Note that this cache is not accurate and might be out of date.
13
14
"""
15
16
def __init__(self, project, observe=True, underlined=False):
17
"""Construct an AutoImport object
18
19
If `observe` is `True`, listen for project changes and update
20
the cache.
21
22
If `underlined` is `True`, underlined names are cached, too.
23
"""
24
self.project = project
25
self.underlined = underlined
26
self.names = project.data_files.read_data('globalnames')
27
if self.names is None:
28
self.names = {}
29
project.data_files.add_write_hook(self._write)
30
# XXX: using a filtered observer
31
observer = resourceobserver.ResourceObserver(
32
changed=self._changed, moved=self._moved, removed=self._removed)
33
if observe:
34
project.add_observer(observer)
35
36
def import_assist(self, starting):
37
"""Return a list of ``(name, module)`` tuples
38
39
This function tries to find modules that have a global name
40
that starts with `starting`.
41
"""
42
# XXX: breaking if gave up! use generators
43
result = []
44
for module in self.names:
45
for global_name in self.names[module]:
46
if global_name.startswith(starting):
47
result.append((global_name, module))
48
return result
49
50
def get_modules(self, name):
51
"""Return the list of modules that have global `name`"""
52
result = []
53
for module in self.names:
54
if name in self.names[module]:
55
result.append(module)
56
return result
57
58
def get_all_names(self):
59
"""Return the list of all cached global names"""
60
result = set()
61
for module in self.names:
62
result.update(set(self.names[module]))
63
return result
64
65
def get_name_locations(self, name):
66
"""Return a list of ``(resource, lineno)`` tuples"""
67
result = []
68
pycore = self.project.pycore
69
for module in self.names:
70
if name in self.names[module]:
71
try:
72
pymodule = pycore.get_module(module)
73
if name in pymodule:
74
pyname = pymodule[name]
75
module, lineno = pyname.get_definition_location()
76
if module is not None:
77
resource = module.get_module().get_resource()
78
if resource is not None and lineno is not None:
79
result.append((resource, lineno))
80
except exceptions.ModuleNotFoundError:
81
pass
82
return result
83
84
def generate_cache(self, resources=None, underlined=None,
85
task_handle=taskhandle.NullTaskHandle()):
86
"""Generate global name cache for project files
87
88
If `resources` is a list of `rope.base.resource.File`\s, only
89
those files are searched; otherwise all python modules in the
90
project are cached.
91
92
"""
93
if resources is None:
94
resources = self.project.pycore.get_python_files()
95
job_set = task_handle.create_jobset(
96
'Generatig autoimport cache', len(resources))
97
for file in resources:
98
job_set.started_job('Working on <%s>' % file.path)
99
self.update_resource(file, underlined)
100
job_set.finished_job()
101
102
def generate_modules_cache(self, modules, underlined=None,
103
task_handle=taskhandle.NullTaskHandle()):
104
"""Generate global name cache for modules listed in `modules`"""
105
job_set = task_handle.create_jobset(
106
'Generatig autoimport cache for modules', len(modules))
107
for modname in modules:
108
job_set.started_job('Working on <%s>' % modname)
109
if modname.endswith('.*'):
110
mod = self.project.pycore.find_module(modname[:-2])
111
if mod:
112
for sub in submodules(mod):
113
self.update_resource(sub, underlined)
114
else:
115
self.update_module(modname, underlined)
116
job_set.finished_job()
117
118
def clear_cache(self):
119
"""Clear all entries in global-name cache
120
121
It might be a good idea to use this function before
122
regenerating global names.
123
124
"""
125
self.names.clear()
126
127
def find_insertion_line(self, code):
128
"""Guess at what line the new import should be inserted"""
129
match = re.search(r'^(def|class)\s+', code)
130
if match is not None:
131
code = code[:match.start()]
132
try:
133
pymodule = self.project.pycore.get_string_module(code)
134
except exceptions.ModuleSyntaxError:
135
return 1
136
testmodname = '__rope_testmodule_rope'
137
importinfo = importutils.NormalImport(((testmodname, None),))
138
module_imports = importutils.get_module_imports(
139
self.project.pycore, pymodule)
140
module_imports.add_import(importinfo)
141
code = module_imports.get_changed_source()
142
offset = code.index(testmodname)
143
lineno = code.count('\n', 0, offset) + 1
144
return lineno
145
146
def update_resource(self, resource, underlined=None):
147
"""Update the cache for global names in `resource`"""
148
try:
149
pymodule = self.project.pycore.resource_to_pyobject(resource)
150
modname = self._module_name(resource)
151
self._add_names(pymodule, modname, underlined)
152
except exceptions.ModuleSyntaxError:
153
pass
154
155
def update_module(self, modname, underlined=None):
156
"""Update the cache for global names in `modname` module
157
158
`modname` is the name of a module.
159
"""
160
try:
161
pymodule = self.project.pycore.get_module(modname)
162
self._add_names(pymodule, modname, underlined)
163
except exceptions.ModuleNotFoundError:
164
pass
165
166
def _module_name(self, resource):
167
return self.project.pycore.modname(resource)
168
169
def _add_names(self, pymodule, modname, underlined):
170
if underlined is None:
171
underlined = self.underlined
172
globals = []
173
if isinstance(pymodule, pyobjects.PyDefinedObject):
174
attributes = pymodule._get_structural_attributes()
175
else:
176
attributes = pymodule.get_attributes()
177
for name, pyname in attributes.items():
178
if not underlined and name.startswith('_'):
179
continue
180
if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)):
181
globals.append(name)
182
if isinstance(pymodule, builtins.BuiltinModule):
183
globals.append(name)
184
self.names[modname] = globals
185
186
def _write(self):
187
self.project.data_files.write_data('globalnames', self.names)
188
189
def _changed(self, resource):
190
if not resource.is_folder():
191
self.update_resource(resource)
192
193
def _moved(self, resource, newresource):
194
if not resource.is_folder():
195
modname = self._module_name(resource)
196
if modname in self.names:
197
del self.names[modname]
198
self.update_resource(newresource)
199
200
def _removed(self, resource):
201
if not resource.is_folder():
202
modname = self._module_name(resource)
203
if modname in self.names:
204
del self.names[modname]
205
206
207
def submodules(mod):
208
if isinstance(mod, resources.File):
209
if mod.name.endswith('.py') and mod.name != '__init__.py':
210
return set([mod])
211
return set()
212
if not mod.has_child('__init__.py'):
213
return set()
214
result = set([mod])
215
for child in mod.get_children():
216
result |= submodules(child)
217
return result
218
219