Path: blob/master/elisp/emacs-for-python/rope-dist/rope/base/resourceobserver.py
1415 views
import os123class ResourceObserver(object):4"""Provides the interface for observing resources56`ResourceObserver`\s can be registered using `Project.7add_observer()`. But most of the time `FilteredResourceObserver`8should be used. `ResourceObserver`\s report all changes passed9to them and they don't report changes to all resources. For10example if a folder is removed, it only calls `removed()` for that11folder and not its contents. You can use12`FilteredResourceObserver` if you are interested in changes only13to a list of resources. And you want changes to be reported on14individual resources.1516"""1718def __init__(self, changed=None, moved=None, created=None,19removed=None, validate=None):20self.changed = changed21self.moved = moved22self.created = created23self.removed = removed24self._validate = validate2526def resource_changed(self, resource):27"""It is called when the resource changes"""28if self.changed is not None:29self.changed(resource)3031def resource_moved(self, resource, new_resource):32"""It is called when a resource is moved"""33if self.moved is not None:34self.moved(resource, new_resource)3536def resource_created(self, resource):37"""Is called when a new resource is created"""38if self.created is not None:39self.created(resource)4041def resource_removed(self, resource):42"""Is called when a new resource is removed"""43if self.removed is not None:44self.removed(resource)4546def validate(self, resource):47"""Validate the existence of this resource and its children.4849This function is called when rope need to update its resource50cache about the files that might have been changed or removed51by other processes.5253"""54if self._validate is not None:55self._validate(resource)565758class FilteredResourceObserver(object):59"""A useful decorator for `ResourceObserver`6061Most resource observers have a list of resources and are62interested only in changes to those files. This class satisfies63this need. It dispatches resource changed and removed messages.64It performs these tasks:6566* Changes to files and folders are analyzed to check whether any67of the interesting resources are changed or not. If they are,68it reports these changes to `resource_observer` passed to the69constructor.70* When a resource is removed it checks whether any of the71interesting resources are contained in that folder and reports72them to `resource_observer`.73* When validating a folder it validates all of the interesting74files in that folder.7576Since most resource observers are interested in a list of77resources that change over time, `add_resource` and78`remove_resource` might be useful.7980"""8182def __init__(self, resource_observer, initial_resources=None,83timekeeper=None):84self.observer = resource_observer85self.resources = {}86if timekeeper is not None:87self.timekeeper = timekeeper88else:89self.timekeeper = ChangeIndicator()90if initial_resources is not None:91for resource in initial_resources:92self.add_resource(resource)9394def add_resource(self, resource):95"""Add a resource to the list of interesting resources"""96if resource.exists():97self.resources[resource] = self.timekeeper.get_indicator(resource)98else:99self.resources[resource] = None100101def remove_resource(self, resource):102"""Add a resource to the list of interesting resources"""103if resource in self.resources:104del self.resources[resource]105106def clear_resources(self):107"""Removes all registered resources"""108self.resources.clear()109110def resource_changed(self, resource):111changes = _Changes()112self._update_changes_caused_by_changed(changes, resource)113self._perform_changes(changes)114115def _update_changes_caused_by_changed(self, changes, changed):116if changed in self.resources:117changes.add_changed(changed)118if self._is_parent_changed(changed):119changes.add_changed(changed.parent)120121def _update_changes_caused_by_moved(self, changes, resource,122new_resource=None):123if resource in self.resources:124changes.add_removed(resource, new_resource)125if new_resource in self.resources:126changes.add_created(new_resource)127if resource.is_folder():128for file in list(self.resources):129if resource.contains(file):130new_file = self._calculate_new_resource(131resource, new_resource, file)132changes.add_removed(file, new_file)133if self._is_parent_changed(resource):134changes.add_changed(resource.parent)135if new_resource is not None:136if self._is_parent_changed(new_resource):137changes.add_changed(new_resource.parent)138139def _is_parent_changed(self, child):140return child.parent in self.resources141142def resource_moved(self, resource, new_resource):143changes = _Changes()144self._update_changes_caused_by_moved(changes, resource, new_resource)145self._perform_changes(changes)146147def resource_created(self, resource):148changes = _Changes()149self._update_changes_caused_by_created(changes, resource)150self._perform_changes(changes)151152def _update_changes_caused_by_created(self, changes, resource):153if resource in self.resources:154changes.add_created(resource)155if self._is_parent_changed(resource):156changes.add_changed(resource.parent)157158def resource_removed(self, resource):159changes = _Changes()160self._update_changes_caused_by_moved(changes, resource)161self._perform_changes(changes)162163def _perform_changes(self, changes):164for resource in changes.changes:165self.observer.resource_changed(resource)166self.resources[resource] = self.timekeeper.get_indicator(resource)167for resource, new_resource in changes.moves.items():168self.resources[resource] = None169if new_resource is not None:170self.observer.resource_moved(resource, new_resource)171else:172self.observer.resource_removed(resource)173for resource in changes.creations:174self.observer.resource_created(resource)175self.resources[resource] = self.timekeeper.get_indicator(resource)176177def validate(self, resource):178changes = _Changes()179for file in self._search_resource_moves(resource):180if file in self.resources:181self._update_changes_caused_by_moved(changes, file)182for file in self._search_resource_changes(resource):183if file in self.resources:184self._update_changes_caused_by_changed(changes, file)185for file in self._search_resource_creations(resource):186if file in self.resources:187changes.add_created(file)188self._perform_changes(changes)189190def _search_resource_creations(self, resource):191creations = set()192if resource in self.resources and resource.exists() and \193self.resources[resource] is None:194creations.add(resource)195if resource.is_folder():196for file in self.resources:197if file.exists() and resource.contains(file) and \198self.resources[file] is None:199creations.add(file)200return creations201202def _search_resource_moves(self, resource):203all_moved = set()204if resource in self.resources and not resource.exists():205all_moved.add(resource)206if resource.is_folder():207for file in self.resources:208if resource.contains(file):209if not file.exists():210all_moved.add(file)211moved = set(all_moved)212for folder in [file for file in all_moved if file.is_folder()]:213if folder in moved:214for file in list(moved):215if folder.contains(file):216moved.remove(file)217return moved218219def _search_resource_changes(self, resource):220changed = set()221if resource in self.resources and self._is_changed(resource):222changed.add(resource)223if resource.is_folder():224for file in self.resources:225if file.exists() and resource.contains(file):226if self._is_changed(file):227changed.add(file)228return changed229230def _is_changed(self, resource):231if self.resources[resource] is None:232return False233return self.resources[resource] != self.timekeeper.get_indicator(resource)234235def _calculate_new_resource(self, main, new_main, resource):236if new_main is None:237return None238diff = resource.path[len(main.path):]239return resource.project.get_resource(new_main.path + diff)240241242class ChangeIndicator(object):243244def get_indicator(self, resource):245"""Return the modification time and size of a `Resource`."""246path = resource.real_path247# on dos, mtime does not change for a folder when files are added248if os.name != 'posix' and os.path.isdir(path):249return (os.path.getmtime(path),250len(os.listdir(path)),251os.path.getsize(path))252return (os.path.getmtime(path),253os.path.getsize(path))254255256class _Changes(object):257258def __init__(self):259self.changes = set()260self.creations = set()261self.moves = {}262263def add_changed(self, resource):264self.changes.add(resource)265266def add_removed(self, resource, new_resource=None):267self.moves[resource] = new_resource268269def add_created(self, resource):270self.creations.add(resource)271272273