def populated_tree(cats):
    """Return the root categories with children populated to any depth.

    Adjacency lists are notoriously inefficient for fetching deeply
    nested trees, and since our dataset will always be reasonably
    small, this method should greatly improve efficiency. Only one
    query is necessary to fetch a tree of any depth. This isn't
    always the solution, but for some situations, it is worthwhile.

    For example, printing the entire tree can be done with one query::

        query = Category.query.options(undefer('media_count'))
        for cat, depth in query.populated_tree().traverse():
            print "    " * depth, cat.name, '(%d)' % cat.media_count

    Without this method, especially with the media_count undeferred,
    this would require a lot of extra queries for nested categories.

    NOTE: If the tree contains circular nesting, the circular portion
          of the tree will be silently omitted from the results.

    """
    children = defaultdict(CategoryList)
    for cat in cats:
        children[cat.parent_id].append(cat)
    for cat in cats:
        set_committed_value(cat, 'children', children[cat.id])
    return children[None]
Example #2
0
def populated_tree(cats):
    """Return the root categories with children populated to any depth.

    Adjacency lists are notoriously inefficient for fetching deeply
    nested trees, and since our dataset will always be reasonably
    small, this method should greatly improve efficiency. Only one
    query is necessary to fetch a tree of any depth. This isn't
    always the solution, but for some situations, it is worthwhile.

    For example, printing the entire tree can be done with one query::

        query = Category.query.options(undefer('media_count'))
        for cat, depth in query.populated_tree().traverse():
            print "    " * depth, cat.name, '(%d)' % cat.media_count

    Without this method, especially with the media_count undeferred,
    this would require a lot of extra queries for nested categories.

    NOTE: If the tree contains circular nesting, the circular portion
          of the tree will be silently omitted from the results.

    """
    children = defaultdict(CategoryList)
    for cat in cats:
        children[cat.parent_id].append(cat)
    for cat in cats:
        set_committed_value(cat, 'children', children[cat.id])
    return children[None]
Example #3
0
def sort_engines(engines):
    """Yield a topological sort of the given list of engines.

    :type engines: list
    :param engines: Unsorted instances of :class:`StorageEngine`.

    """
    # Partial ordering of engine classes, keys come before values.
    edges = defaultdict(set)

    # Collection of engine instances grouped by their class.
    engine_objs = defaultdict(set)

    # Find all edges between registered engine classes
    for engine in engines:
        engine_cls = engine.__class__
        engine_objs[engine_cls].add(engine)
        for edge_cls in engine.try_before:
            edges[edge_cls].add(engine_cls)
            for edge_cls_implementation in edge_cls:
                edges[edge_cls_implementation].add(engine_cls)
        for edge_cls in engine.try_after:
            edges[engine_cls].add(edge_cls)
            for edge_cls_implementation in edge_cls:
                edges[engine_cls].add(edge_cls_implementation)

    # Iterate over the engine classes
    todo = set(engine_objs.iterkeys())
    while todo:
        # Pull out classes that have no unsatisfied edges
        output = set()
        for engine_cls in todo:
            if not todo.intersection(edges[engine_cls]):
                output.add(engine_cls)
        if not output:
            raise RuntimeError('Circular dependency detected.')
        todo.difference_update(output)

        # Collect all the engine instances we'll be returning in this round,
        # ordering them by ID to give consistent results each time we run this.
        output_instances = []
        for engine_cls in output:
            output_instances.extend(engine_objs[engine_cls])
        output_instances.sort(key=attrgetter('id'))

        for engine in output_instances:
            yield engine
Example #4
0
def sort_engines(engines):
    """Yield a topological sort of the given list of engines.

    :type engines: list
    :param engines: Unsorted instances of :class:`StorageEngine`.

    """
    # Partial ordering of engine classes, keys come before values.
    edges = defaultdict(set)

    # Collection of engine instances grouped by their class.
    engine_objs = defaultdict(set)

    # Find all edges between registered engine classes
    for engine in engines:
        engine_cls = engine.__class__
        engine_objs[engine_cls].add(engine)
        for edge_cls in engine.try_before:
            edges[edge_cls].add(engine_cls)
            for edge_cls_implementation in edge_cls:
                edges[edge_cls_implementation].add(engine_cls)
        for edge_cls in engine.try_after:
            edges[engine_cls].add(edge_cls)
            for edge_cls_implementation in edge_cls:
                edges[engine_cls].add(edge_cls_implementation)

    # Iterate over the engine classes
    todo = set(engine_objs.iterkeys())
    while todo:
        # Pull out classes that have no unsatisfied edges
        output = set()
        for engine_cls in todo:
            if not todo.intersection(edges[engine_cls]):
                output.add(engine_cls)
        if not output:
            raise RuntimeError('Circular dependency detected.')
        todo.difference_update(output)

        # Collect all the engine instances we'll be returning in this round,
        # ordering them by ID to give consistent results each time we run this.
        output_instances = []
        for engine_cls in output:
            output_instances.extend(engine_objs[engine_cls])
        output_instances.sort(key=attrgetter('id'))

        for engine in output_instances:
            yield engine
Example #5
0
class AbstractMetaClass(type):
    """
    Abstract Class Manager

    This combines concepts from the Trac ComponentMeta class and
    Python 2.6's abc module:

        * http://www.python.org/dev/peps/pep-3119/#specification
        * http://svn.python.org/view/python/trunk/Lib/abc.py?view=markup
        * http://trac.edgewall.org/browser/trunk/trac/core.py#L85

    """
    _registry = defaultdict(list)
    _abstracts = {}
    _observers = {}

    def __new__(mcls, name, bases, namespace):
        """Create a class object for an abstract class or its implementation.

        For abstract classes, we store the set of abstract attributes
        that have been defined. We use this data in :meth:`register`
        to validate all subclasses to ensure it has a complete
        implementation.

        """
        cls = type.__new__(mcls, name, bases, namespace)
        abstracts = set(key for key, value in namespace.iteritems()
                        if getattr(value, '_isabstract', False))
        for base in bases:
            for name in AbstractMetaClass._abstracts.get(base, ()):
                cls_attr = getattr(cls, name, None)
                if getattr(cls_attr, '_isabstract', False):
                    abstracts.add(name)
        AbstractMetaClass._abstracts[cls] = abstracts
        return cls

    def register(cls, subclass):
        """Register an implementation of the abstract class.

        :param cls: The abstract class
        :param subclass: A complete implementation of the abstract class.
        :raises ImplementationError: If the subclass contains any
            unimplemented abstract methods or properties.

        """
        # If an attr was abstract when the class was created, check again
        # to see if it was implemented after the fact (by monkepatch etc).
        missing = []
        for name in AbstractMetaClass._abstracts.get(subclass, ()):
            attr = getattr(subclass, name)
            if getattr(attr, '_isabstract', False):
                missing.append(name)
        if missing:
            raise ImplementationError(
                'Cannot register %r under %r because it contains abstract '
                'methods/properties: %s' % (subclass, cls, ', '.join(missing)))
        # Register the subclass, calling observers all the way up the
        # inheritance tree as we go.
        for base in chain((subclass, ), cls.__mro__):
            if base.__class__ is AbstractMetaClass:
                if base is subclass:
                    AbstractMetaClass._registry[base]
                else:
                    AbstractMetaClass._registry[base].append(subclass)
                for observer in AbstractMetaClass._observers.get(base, ()):
                    observer(subclass)

    def add_register_observer(cls, callable):
        """Notify this callable when a subclass of this abstract is registered.

        This is useful when some action must be taken for each new
        implementation of an abstract class. This observer will also be
        called any time any of its sub-abstract classes are implemented.

        :param cls: The abstract class
        :param callable: A function that expects a subclass as its
            first and only argument.

        """
        AbstractMetaClass._observers.setdefault(cls, []).append(callable)

    def remove_register_observer(cls, callable):
        """Cancel notifications to this callable for this abstract class.

        :param cls: The abstract class
        :param callable: A function that expects a subclass as its
            first and only argument.
        :raises ValueError: If the callable has not been registered.

        """
        AbstractMetaClass._observers.setdefault(cls, []).remove(callable)

    def __iter__(cls):
        """Iterate over all implementations of the given abstract class."""
        return iter(AbstractMetaClass._registry[cls])

    def __contains__(cls, subclass):
        """Return True if the first class is a parent of the second class."""
        return subclass in AbstractMetaClass._registry[cls]