Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/emacs-for-python/rope-dist/rope/base/resourceobserver.py
1415 views
1
import os
2
3
4
class ResourceObserver(object):
5
"""Provides the interface for observing resources
6
7
`ResourceObserver`\s can be registered using `Project.
8
add_observer()`. But most of the time `FilteredResourceObserver`
9
should be used. `ResourceObserver`\s report all changes passed
10
to them and they don't report changes to all resources. For
11
example if a folder is removed, it only calls `removed()` for that
12
folder and not its contents. You can use
13
`FilteredResourceObserver` if you are interested in changes only
14
to a list of resources. And you want changes to be reported on
15
individual resources.
16
17
"""
18
19
def __init__(self, changed=None, moved=None, created=None,
20
removed=None, validate=None):
21
self.changed = changed
22
self.moved = moved
23
self.created = created
24
self.removed = removed
25
self._validate = validate
26
27
def resource_changed(self, resource):
28
"""It is called when the resource changes"""
29
if self.changed is not None:
30
self.changed(resource)
31
32
def resource_moved(self, resource, new_resource):
33
"""It is called when a resource is moved"""
34
if self.moved is not None:
35
self.moved(resource, new_resource)
36
37
def resource_created(self, resource):
38
"""Is called when a new resource is created"""
39
if self.created is not None:
40
self.created(resource)
41
42
def resource_removed(self, resource):
43
"""Is called when a new resource is removed"""
44
if self.removed is not None:
45
self.removed(resource)
46
47
def validate(self, resource):
48
"""Validate the existence of this resource and its children.
49
50
This function is called when rope need to update its resource
51
cache about the files that might have been changed or removed
52
by other processes.
53
54
"""
55
if self._validate is not None:
56
self._validate(resource)
57
58
59
class FilteredResourceObserver(object):
60
"""A useful decorator for `ResourceObserver`
61
62
Most resource observers have a list of resources and are
63
interested only in changes to those files. This class satisfies
64
this need. It dispatches resource changed and removed messages.
65
It performs these tasks:
66
67
* Changes to files and folders are analyzed to check whether any
68
of the interesting resources are changed or not. If they are,
69
it reports these changes to `resource_observer` passed to the
70
constructor.
71
* When a resource is removed it checks whether any of the
72
interesting resources are contained in that folder and reports
73
them to `resource_observer`.
74
* When validating a folder it validates all of the interesting
75
files in that folder.
76
77
Since most resource observers are interested in a list of
78
resources that change over time, `add_resource` and
79
`remove_resource` might be useful.
80
81
"""
82
83
def __init__(self, resource_observer, initial_resources=None,
84
timekeeper=None):
85
self.observer = resource_observer
86
self.resources = {}
87
if timekeeper is not None:
88
self.timekeeper = timekeeper
89
else:
90
self.timekeeper = ChangeIndicator()
91
if initial_resources is not None:
92
for resource in initial_resources:
93
self.add_resource(resource)
94
95
def add_resource(self, resource):
96
"""Add a resource to the list of interesting resources"""
97
if resource.exists():
98
self.resources[resource] = self.timekeeper.get_indicator(resource)
99
else:
100
self.resources[resource] = None
101
102
def remove_resource(self, resource):
103
"""Add a resource to the list of interesting resources"""
104
if resource in self.resources:
105
del self.resources[resource]
106
107
def clear_resources(self):
108
"""Removes all registered resources"""
109
self.resources.clear()
110
111
def resource_changed(self, resource):
112
changes = _Changes()
113
self._update_changes_caused_by_changed(changes, resource)
114
self._perform_changes(changes)
115
116
def _update_changes_caused_by_changed(self, changes, changed):
117
if changed in self.resources:
118
changes.add_changed(changed)
119
if self._is_parent_changed(changed):
120
changes.add_changed(changed.parent)
121
122
def _update_changes_caused_by_moved(self, changes, resource,
123
new_resource=None):
124
if resource in self.resources:
125
changes.add_removed(resource, new_resource)
126
if new_resource in self.resources:
127
changes.add_created(new_resource)
128
if resource.is_folder():
129
for file in list(self.resources):
130
if resource.contains(file):
131
new_file = self._calculate_new_resource(
132
resource, new_resource, file)
133
changes.add_removed(file, new_file)
134
if self._is_parent_changed(resource):
135
changes.add_changed(resource.parent)
136
if new_resource is not None:
137
if self._is_parent_changed(new_resource):
138
changes.add_changed(new_resource.parent)
139
140
def _is_parent_changed(self, child):
141
return child.parent in self.resources
142
143
def resource_moved(self, resource, new_resource):
144
changes = _Changes()
145
self._update_changes_caused_by_moved(changes, resource, new_resource)
146
self._perform_changes(changes)
147
148
def resource_created(self, resource):
149
changes = _Changes()
150
self._update_changes_caused_by_created(changes, resource)
151
self._perform_changes(changes)
152
153
def _update_changes_caused_by_created(self, changes, resource):
154
if resource in self.resources:
155
changes.add_created(resource)
156
if self._is_parent_changed(resource):
157
changes.add_changed(resource.parent)
158
159
def resource_removed(self, resource):
160
changes = _Changes()
161
self._update_changes_caused_by_moved(changes, resource)
162
self._perform_changes(changes)
163
164
def _perform_changes(self, changes):
165
for resource in changes.changes:
166
self.observer.resource_changed(resource)
167
self.resources[resource] = self.timekeeper.get_indicator(resource)
168
for resource, new_resource in changes.moves.items():
169
self.resources[resource] = None
170
if new_resource is not None:
171
self.observer.resource_moved(resource, new_resource)
172
else:
173
self.observer.resource_removed(resource)
174
for resource in changes.creations:
175
self.observer.resource_created(resource)
176
self.resources[resource] = self.timekeeper.get_indicator(resource)
177
178
def validate(self, resource):
179
changes = _Changes()
180
for file in self._search_resource_moves(resource):
181
if file in self.resources:
182
self._update_changes_caused_by_moved(changes, file)
183
for file in self._search_resource_changes(resource):
184
if file in self.resources:
185
self._update_changes_caused_by_changed(changes, file)
186
for file in self._search_resource_creations(resource):
187
if file in self.resources:
188
changes.add_created(file)
189
self._perform_changes(changes)
190
191
def _search_resource_creations(self, resource):
192
creations = set()
193
if resource in self.resources and resource.exists() and \
194
self.resources[resource] is None:
195
creations.add(resource)
196
if resource.is_folder():
197
for file in self.resources:
198
if file.exists() and resource.contains(file) and \
199
self.resources[file] is None:
200
creations.add(file)
201
return creations
202
203
def _search_resource_moves(self, resource):
204
all_moved = set()
205
if resource in self.resources and not resource.exists():
206
all_moved.add(resource)
207
if resource.is_folder():
208
for file in self.resources:
209
if resource.contains(file):
210
if not file.exists():
211
all_moved.add(file)
212
moved = set(all_moved)
213
for folder in [file for file in all_moved if file.is_folder()]:
214
if folder in moved:
215
for file in list(moved):
216
if folder.contains(file):
217
moved.remove(file)
218
return moved
219
220
def _search_resource_changes(self, resource):
221
changed = set()
222
if resource in self.resources and self._is_changed(resource):
223
changed.add(resource)
224
if resource.is_folder():
225
for file in self.resources:
226
if file.exists() and resource.contains(file):
227
if self._is_changed(file):
228
changed.add(file)
229
return changed
230
231
def _is_changed(self, resource):
232
if self.resources[resource] is None:
233
return False
234
return self.resources[resource] != self.timekeeper.get_indicator(resource)
235
236
def _calculate_new_resource(self, main, new_main, resource):
237
if new_main is None:
238
return None
239
diff = resource.path[len(main.path):]
240
return resource.project.get_resource(new_main.path + diff)
241
242
243
class ChangeIndicator(object):
244
245
def get_indicator(self, resource):
246
"""Return the modification time and size of a `Resource`."""
247
path = resource.real_path
248
# on dos, mtime does not change for a folder when files are added
249
if os.name != 'posix' and os.path.isdir(path):
250
return (os.path.getmtime(path),
251
len(os.listdir(path)),
252
os.path.getsize(path))
253
return (os.path.getmtime(path),
254
os.path.getsize(path))
255
256
257
class _Changes(object):
258
259
def __init__(self):
260
self.changes = set()
261
self.creations = set()
262
self.moves = {}
263
264
def add_changed(self, resource):
265
self.changes.add(resource)
266
267
def add_removed(self, resource, new_resource=None):
268
self.moves[resource] = new_resource
269
270
def add_created(self, resource):
271
self.creations.add(resource)
272
273