def test_remove(self): objects = OrderedSet([1, 2, 3]) self.assertEqual(3, len(objects)) objects.remove(2) self.assertEqual(2, len(objects)) self.assertEqual(1, objects[0]) self.assertEqual(3, objects[1]) objects.remove(3) self.assertEqual(1, len(objects)) self.assertEqual(1, objects[0]) objects.add(1) self.assertEqual(1, len(objects)) self.assertEqual(1, objects[0]) objects.remove(1) self.assertEqual(0, len(objects))
class ModuleDescriptor(object): def __init__(self, module_code): super(ModuleDescriptor, self).__init__() self.__module_code = module_code self.__mtime = None self.modified() self.__dependencies = OrderedSet() self.__reverse_dependencies = OrderedSet() def __str__(self): return "<ModuleDescriptor '%s' (%s)>" % (self.name, self.filename) def __eq__(self, other): return self is other or (isinstance(other, type(self)) and self.module_code == other.module_code) def __hash__(self): return hash(self.module_code) def describe(self, indent=1, width=80, depth=None): """ Return the formatted representation of ``ModuleDescriptor`` as a string. *indent*, *width* and *depth* will be passed to the ``PrettyPrinter`` constructor as formatting parameters. """ def _format_monitor_list(modules): import re from pprint import pformat s = pformat(list(m.name for m in modules), indent=indent, width=width, depth=depth) # bug? return re.sub(r"\[( +)", "[\n\\1 ", s) messages = [] messages.append("%s: %s" % (self.name, relativepath(self.filename))) messages.append(" Dependencies: %s" % _format_monitor_list(self.__dependencies)) messages.append(" Reverse: %s" % _format_monitor_list(self.__reverse_dependencies)) return "\n".join(messages) @property def dependencies(self): return tuple(self.__dependencies) @property def reverse_dependencies(self): return tuple(self.__reverse_dependencies) @property def module_code(self): return self.__module_code @property def name(self): return self.module_code.name @property def package_name(self): return self.module_code.package_name @property def filename(self): return self.module_code.filename @property def context(self): return self.module_code.context def reload(self, descriptors, co=None): """ Reload module code, update dependency graph """ LOGGER.info("Reload module descriptor '%s' at %s" % (self.name, relativepath(self.filename))) try: self.module_code.reload(co) except SyntaxError: # SyntaxError is OK LOGGER.warn("SyntaxError found in %s" % self.filename, exc_info=True) else: self.update_dependencies(descriptors) def modified(self): """Update modification time and return ``True`` if modified""" mtime = self.__mtime try: mtime = os.path.getmtime(self.filename) return self.__mtime is None or mtime > self.__mtime finally: self.__mtime = mtime def add_dependency(self, descriptor): self.__dependencies.append(descriptor) descriptor.add_reverse_dependency(self) def add_reverse_dependency(self, descriptor): self.__reverse_dependencies.append(descriptor) def remove_reverse_dependencies(self, descriptor): self.__reverse_dependencies.remove(descriptor) def update_dependencies(self, descriptors): LOGGER.debug("Update dependencies of '%s'" % self.name) _update_module_dependencies(self, descriptors) def clear_dependencies(self): for d in self.__dependencies: d.remove_reverse_dependencies(self) self.__dependencies.clear() def walk_dependency_graph(self, reverse=False): """ Walking on the module dependency graph. *reverse* is a boolean value. If set to ``True``, then the walking is in an imported module (self included) to a module imports order. """ if reverse: graph_name = "reverse_dependencies" else: graph_name = "dependencies" # self first yield self # Use Breadth First Search (BFS) algorithm vqueue = [self] discovered = set(vqueue) while vqueue: u = vqueue.pop() for v in getattr(u, graph_name): if v not in discovered: discovered.add(v) vqueue.append(v) yield v