OLD | NEW |
(Empty) | |
| 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] != \ |
| 235 self.timekeeper.get_indicator(resource) |
| 236 |
| 237 def _calculate_new_resource(self, main, new_main, resource): |
| 238 if new_main is None: |
| 239 return None |
| 240 diff = resource.path[len(main.path):] |
| 241 return resource.project.get_resource(new_main.path + diff) |
| 242 |
| 243 |
| 244 class ChangeIndicator(object): |
| 245 |
| 246 def get_indicator(self, resource): |
| 247 """Return the modification time and size of a `Resource`.""" |
| 248 path = resource.real_path |
| 249 # on dos, mtime does not change for a folder when files are added |
| 250 if os.name != 'posix' and os.path.isdir(path): |
| 251 return (os.path.getmtime(path), |
| 252 len(os.listdir(path)), |
| 253 os.path.getsize(path)) |
| 254 return (os.path.getmtime(path), |
| 255 os.path.getsize(path)) |
| 256 |
| 257 |
| 258 class _Changes(object): |
| 259 |
| 260 def __init__(self): |
| 261 self.changes = set() |
| 262 self.creations = set() |
| 263 self.moves = {} |
| 264 |
| 265 def add_changed(self, resource): |
| 266 self.changes.add(resource) |
| 267 |
| 268 def add_removed(self, resource, new_resource=None): |
| 269 self.moves[resource] = new_resource |
| 270 |
| 271 def add_created(self, resource): |
| 272 self.creations.add(resource) |
OLD | NEW |