Commits

Chris Mutel committed 42f3004

Whitespace cleanup

  • Participants
  • Parent commits 95088d7

Comments (0)

Files changed (14)

File django_dag/TODO

 template tags and filters
-pygraph
+pygraph
+populate_content_object_caches in content_type_utils

File django_dag/content_type_utils.py

 from django.contrib.contenttypes.models import ContentType
 import itertools
 
+
 def fetch_model_dict(model, ids, fields=None):
     """
     Fetches a dict of model details for model instances with the given
             in model._default_manager.filter(id__in=ids).values(
                 *itertools.chain((id_attr,), fields)))
 
+
 def populate_content_object_caches(generic_related_objects, model_fields=None):
     """
     Retrieves ``ContentType`` and content objects for the given list of

File django_dag/errors.py

 class CircularReferenceError(StandardError):
     pass
 
+
 class SeparateGraphsError(StandardError):
     pass
 
+
 class MultipleReferencesError(StandardError):
     pass
 
+
 class DuplicateEdgeError(StandardError):
     pass
 
+
 class MissingGraphError(StandardError):
-    pass
+    pass

File django_dag/management/__init__.py

 from django.db.models import signals
 from django_dag import models
 
+
 def create_tables_and_triggers(sender, **kwargs):
     if sender != models:
         return
     cursor = connection.cursor()
-    
+
     # Test if plpgsql is installed
     TEST_PLPGSQL_SQL = """select count(*) from pg_language where lanname = 'plpgsql'"""
     cursor.execute(TEST_PLPGSQL_SQL)
         INSTALL_PLPGSQL_SQL = """CREATE TRUSTED PROCEDURAL LANGUAGE 'plpgsql' HANDLER plpgsql_call_handler VALIDATOR plpgsql_validator"""
         cursor.execute(INSTALL_PLPGSQL_SQL)
         transaction.commit_unless_managed()
-    
+
     # Exit if already installed
     TEST_FUNCTION_EXISTS = """SELECT count(*) FROM pg_proc where proname = 'enforce_acyclicity'"""
     cursor.execute(TEST_FUNCTION_EXISTS)
     if cursor.fetchone()[0]:
         return
-    
+
     try:
         transaction.enter_transaction_management()
         transaction.managed(True)
         # Set default depth to 0 for database triggers
         DEFAULT_DEPTH_SQL = """alter table dag_transitive_closure alter column depth set default 0"""
         cursor.execute(DEFAULT_DEPTH_SQL)
-    
+
         ADD_EDGE_CHECK_SQL = """ALTER TABLE dag_edge ADD CHECK (node_to_id <> node_from_id)"""
         cursor.execute(ADD_EDGE_CHECK_SQL)
-    
+
         TRANSITIVE_CLOSURE_INDEX_1_SQL = """create index idx_trans_from_graph_to on dag_transitive_closure(node_from_id, graph_id, node_to_id)"""
         cursor.execute(TRANSITIVE_CLOSURE_INDEX_1_SQL)
-    
+
         TRANSITIVE_CLOSURE_INDEX_2_SQL = """create index idx_trans_to_graph on dag_transitive_closure(node_to_id, graph_id)"""
         cursor.execute(TRANSITIVE_CLOSURE_INDEX_2_SQL)
-    
+
         ENFORCE_ACYCLICALITY_FUNCTION = """create function enforce_acyclicity() returns trigger as
         $$
         begin
             $$ language plpgsql"""
         REMOVE_IMPLIED_EDGES_TRIGGER = """create trigger trig_remove_implied_edges after delete on dag_edge
             for each row execute procedure remove_implied_edges()"""
-    
+
         cursor.execute(ADD_IMPLIED_EDGES_FUNCTION)
         cursor.execute(ADD_IMPLIED_EDGES_TRIGGER)
         cursor.execute(REMOVE_IMPLIED_EDGES_FUNCTION)
         import traceback        
         print traceback.print_exc()
         transaction.rollback()
-    
+
     finally:
         transaction.leave_transaction_management()
 
 signals.post_syncdb.connect(create_tables_and_triggers, 
-    dispatch_uid='django_dag.starter.init.tables')
+    dispatch_uid='django_dag.starter.init.tables')

File django_dag/mixins.py

 from models import TransitiveClosure, Node, Edge
 from errors import MultipleReferencesError
 
+
 class GraphMixin(models.Model):
     """Mixin class that adds methods to define, explore, and manipulate a directed acyclic graph."""
     class Meta:
         abstract = True
-    
+
     default_graph = None
-    
+
     @property
     def content_type(self):
         """Get Content Type for this object."""
             self._content_type = ContentType.objects.get(
                 app_label=self._meta.app_label, model=self._meta.module_name)
         return self._content_type
-    
+
     def node(self, graph=None):
         """Get node associated with object, if it exists. If not, return None."""
         graph = self.default_graph if not graph else graph
             return Node.objects.create(content_type=self.content_type, 
                 object_id=self.pk, graph=graph)
         elif node.count() > 1:
-            raise MultipleReferencesError, "This object is associated " + \
-                "with multiple nodes. Please specify the graph."
+            raise MultipleReferencesError("This object is associated " + \
+                "with multiple nodes. Please specify the graph.")
         return node[0]
 
     def get_parents(self, graph=None, include_self=False):
         if include_self:
             objs.insert(0, self)
         return objs
-    
+
     def get_children(self, graph=None, include_self=False):
         """Get child objects for this object."""
         graph = self.default_graph if not graph else graph
         if include_self:
             objs.insert(0, self)
         return objs
-    
+
     def get_ancestors(self, graph=None, include_self=False):
         """Get ancestor objects this object, sorted by depth."""
         graph = self.default_graph if not graph else graph
         if include_self:
             objs.insert(0, self)
         return objs
-    
+
     def get_descendants(self, graph=None, include_self=False):
         """Get descendant objects this object, sorted by depth."""
         graph = self.default_graph if not graph else graph
         if include_self:
             objs.insert(0, self)
         return objs
-    
+
     def get_parent_count(self, graph=None):
         """Get count of parent objects for this object."""
         graph = self.default_graph if not graph else graph
         return TransitiveClosure.objects.filter(node_from=self.node(
             graph), depth=0).count()
-    
+
     def get_ancestor_count(self, graph=None):
         """Get count of ancestor objects for this object."""
         graph = self.default_graph if not graph else graph
         return TransitiveClosure.objects.filter(node_from=self.node(
             graph)).values("node_to").distinct().count()
-    
+
     def get_child_count(self, graph=None):
         """Get count of child objects for this object."""
         graph = self.default_graph if not graph else graph
         return TransitiveClosure.objects.filter(node_to=self.node(
             graph), depth=0).count()
-    
+
     def get_descendant_count(self, graph=None):
         """Get count of descendant objects for this object."""
         graph = self.default_graph if not graph else graph
         return TransitiveClosure.objects.filter(node_to=self.node(
             graph)).values("node_from").distinct().count()
-    
+
     def is_child_node(self, graph=None):
         """Is this node a child node?"""
         graph = self.default_graph if not graph else graph
         return self.get_parent_count(graph) > 0
-    
+
     def is_root_node(self, graph=None):
         """Is this node a root node (i.e. has no parents)?"""
         graph = self.default_graph if not graph else graph
         return self.get_parent_count(graph) == 0
-    
+
     def is_leaf_node(self, graph=None):
         """Is this node a leaf node (i.e. has no children)?"""
         graph = self.default_graph if not graph else graph
 
     def add_parent(self, obj, graph=None):
         """Add a directed link between obj (as parent) and self (as child).
-        
+
         *obj* must be graph-aware, i.e. have a node method."""
         graph = self.default_graph if not graph else graph
         node_to = obj.node(graph=graph)
 
     def add_child(self, obj, graph=None):
         """Add a directed link between self (as parent) and obj (as child).
-        
+
         *obj* must be graph-aware, i.e. have a node method."""
         graph = self.default_graph if not graph else graph
         node_to = self.node(graph=graph)
 
     def remove_child(self, obj, graph=None):
         """Remove a directed link between self (as parent) and obj (as child)
-        
+
         *obj* must be graph-aware, i.e. have a node method."""
         graph = self.default_graph if not graph else graph
         node_to = self.node(graph=graph)
 
     def remove_parent(self, obj, graph=None):
         """Remove a directed link between self (as child) and obj (as parent)
-        
+
         *obj* must be graph-aware, i.e. have a node method."""
         graph = self.default_graph if not graph else graph
         node_to = obj.node(graph=graph)

File django_dag/models.py

 from django.db import models
-from django.utils.translation import ugettext as _
 from django.contrib.contenttypes import generic
 from django.contrib.contenttypes.models import ContentType
 from errors import *
 from utils import create_objects_iterable, flatten
 
+
 class Graph(models.Model):
     name = models.TextField(unique=True)
-    
+
     class Meta:
         db_table = "dag_graph"
-    
+
     def __unicode__(self):
         return "Graph: %s" % self.name
-    
+
     @property
     def is_heterogeneous(self):
         return self.content_types_number > 1
-    
+
     @property
     def content_types_number(self):
         return Node.objects.filter(graph=self).values('content_type'
 
     def combine_graphs(self, other, delete=True):
         if not isinstance(other, Graph):
-            raise TypeError, "Must combine with Graph instance."
-        
+            raise TypeError("Must combine with Graph instance.")
+
         # Check for multiple references
         first_nodes = Node.objects.filter(graph__id=self.id).values_list("id", 
             flat=True)
         second_nodes = Node.objects.filter(graph__id=other.id).values_list( 
             "id", flat=True)
         if list(set(first_nodes).intersection(set(second_nodes))):
-            raise MultipleReferencesError, \
-                "Can't combine graphs with the same node"
-        
+            raise MultipleReferencesError("Can't combine graphs with the" + \
+                " same node")
+
         edges = Edge.objects.filter(graph=other).values("node_from", 
             "node_to")
         nodes = Node.objects.filter(graph=other)
         nodes.update(graph=self)
-        
+
         # Will delete TransitiveClosure objects on cascade
         Edge.objects.filter(graph=other).delete()
-        
+
         # Cache in dictionary for speedy lookups
         node_dict = {}
         for d in edges:
             if d["node_to"] not in node_dict:
                 d["node_to"] = Node.objects.get(id=d["node_to"])
             Edge.objects.create(d["node_from"], d["node_to"], graph=self)
-        
+
         if delete:
             other.delete()
-    
+
     @property
     def root_nodes(self):
         """Get root nodes for a graph"""
     def root_node_objects(self):
         """Get objects for root nodes helper"""
         root_nodes = self.root_nodes
-    
+
         tc_query = root_nodes.values_list('graph', 'id', 'content_type', 
             'object_id')
-    
+
         # Create dictionary with content type ids as keys, and objects ids 
         # as values
         content_type_dict = {}
     content_type = models.ForeignKey(ContentType, null=True, blank=True)
     object_id = models.PositiveIntegerField(null=True, blank=True)
     reference = generic.GenericForeignKey('content_type', 'object_id')
-    
+
     class Meta:
         db_table = "dag_node"
         unique_together = ("graph", "content_type", "object_id")
-    
+
     def save(self, *args, **kwargs):
         # Check for content_type / graph uniqueness upon obj creation
         if not self.id and self.content_type and self.object_id:
             if Node.objects.filter(content_type=self.content_type, 
                     object_id=self.object_id, graph=self.graph).count():
-                raise MultipleReferencesError, "An existing node already " + \
-                    "exists for this object reference and graph"
+                raise MultipleReferencesError("An existing node already " + \
+                    "exists for this object reference and graph")
         super(Node, self).save(*args, **kwargs)
-    
+
     def __unicode__(self):
         if self.object_id:
             return "Node: %s" % self.reference
         else:
             return "<Node object with id %s at address %s>" % (self.id, 
                 id(self))
-    
+
     def link_to(self, other_node):
         return Edge.objects.create(self, other_node, graph=self.graph)
-    
+
     def link_from(self, other_node):
         return Edge.objects.create(other_node, self, graph=self.graph)
 
 
 class EdgeManager(models.Manager):
     def update(self):
-        raise NotImplementedError, \
-            "Edges can't be updated - they must be deleted and re-created."
-    
+        raise NotImplementedError("Edges can't be updated - they must be" + \
+            " deleted and re-created.")
+
     def _parse_to_node(self, node, graph):
         # Make sure objects exist in database
         try:
             # Is this the best way to test if an object has been saved?
             assert node.pk
         except AssertionError:
-            raise AttributeError, \
-                "Can only link nodes already saved in database"
-        
+            raise AttributeError("Can only link nodes already saved in" + \
+                " database")
+
         if isinstance(node, Node):
             return node
         try:
                 return _node
             else:
                 if not graph:
-                    raise MissingGraphError, \
-                        "Must specify graph when creating new node."
+                    raise MissingGraphError("Must specify graph when " + \
+                        "creating new node.")
                 return Node.objects.create(graph=graph, object_id=node.id, 
                     content_type=node.content_type)
         except AttributeError:
             # Not registered with DAG mixin
             if not graph:
-                raise MissingGraphError, \
-                    "Must specify graph when creating new node."
+                raise MissingGraphError("Must specify graph when creating " + \
+                    "new node.")
             content_type = ContentType.objects.get(
                 app_label=node._meta.app_label, model=node._meta.module_name)
             return Node.objects.create(graph=graph, object_id=node.id, 
                 content_type=content_type)
-    
+
     def create(self, node_from, node_to, graph=None, combine_graphs=False):
         # Check that nodes are of correct type
         node_from = self._parse_to_node(node_from, graph)
         node_to = self._parse_to_node(node_to, graph)
-        
+
         if not graph:
             graph = node_to.graph
-        
+
         different_graphs = node_from.graph != node_to.graph
         if different_graphs and not combine_graphs:
             raise SeparateGraphsError
             node_to = Node.objects.get(id=node_to.id)
             node_from = Node.objects.get(id=node_from.id)
             graph = node_to.graph
-        
+
         return super(EdgeManager, self).create(graph=graph, 
             node_from=node_from, node_to=node_to)
 
     graph = models.ForeignKey(Graph, blank=True)
     node_from = models.ForeignKey(Node, related_name="from")
     node_to = models.ForeignKey(Node, related_name="to")
-    
+
     def save(self, *args, **kwargs):
         if self.id:
-            raise NotImplementedError, \
-                "Edges can only be created or deleted, not updated."
-        
+            raise NotImplementedError("Edges can only be created or " + \
+                "deleted, not updated.")
+
         if self.node_from == self.node_to:
             raise CircularReferenceError
-        
+
         if self.node_from.graph != self.node_to.graph:
             raise SeparateGraphsError
-        
+
         if Edge.objects.filter(node_from=self.node_from, node_to=self.node_to, 
                 graph=self.graph).count():
             raise DuplicateEdgeError
-        
+
         if not Edge.check_acyclical(self.node_from, self.node_to):
             raise CircularReferenceError
-        
+
         kwargs["force_insert"] = True
         super(Edge, self).save(*args, **kwargs)
 
 
     class Meta:
         db_table = "dag_transitive_closure"
-    
+
     def __unicode__(self):
         return "Transitive Closure link: %s to %s" % (self.node_from, 
             self.node_to)
-

File django_dag/sql.py

 import os
-from django.db import connections, transaction
+from django.db import connections
+
 
 def sql(obj, params=(), using="default", as_dict=False):
     """
     else:
         return cursor
 
+
 def dict_cursor(cursor):
     """Turn SQL tuples into dictionaries. Returns generator."""
     description = [x[0] for x in cursor.description]
     for row in cursor:
         yield dict(zip(description, row))
-

File django_dag/tests/__init__.py

 from database import DatabaseTestCase
 from content_types import ContentTypeTestCase
-from different_graphs import DifferentGraphsTestCase
+from different_graphs import DifferentGraphsTestCase

File django_dag/tests/content_types.py

 from models import *
 from django.test import TestCase
 
+
 class ContentTypeTestCase(TestCase):
 
     def test_basic(self):
         self.assertEqual(zurich.get_descendants(), [geneva, winterthur, 
             glarus])
         self.assertFalse(g.is_heterogeneous)
-    
+
     def test_multiple_ct_references(self):
         g = Graph.objects.create(name="graph")
         zurich = City.objects.create(name="Zurich")
         n2 = Node(graph=g)
         n2.reference = zurich
         self.assertRaises(MultipleReferencesError, n2.save)
-    
+
     def test_heterogeneous_trees_1(self):
         """Test heterogeneous trees with manual node references"""
         g = Graph.objects.create(name="graph")
         Edge.objects.create(green_node, bern_node)
         self.assertEqual(zurich.get_descendants(), [blue, bern, green])
         self.assertTrue(g.is_heterogeneous)
-    
+
     def test_heterogeneous_trees_2(self):
         """Test heterogeneous trees with automatic node references"""
         g = Graph.objects.create(name="graph")
         Edge.objects.create(bern.node(g), green.node(g), graph=g)
         self.assertEqual(zurich.get_ancestors(graph=g), [blue, bern, green])
         self.assertTrue(g.is_heterogeneous)
-    
+
     def test_blank_nodes(self):
         g = Graph.objects.create(name="graph")
         zurich = City.objects.create(name="Zurich")
         Edge.objects.create(node_from=blank_node, node_to=bern.node(g), 
             graph=g)
         self.assertEqual(zurich.get_ancestors(), [blank_node, bern])
-    
+
     def test_edge_creation_without_node_creation(self):
         g = Graph.objects.create(name="graph")
         zurich = City.objects.create(name="Zurich")
         Edge.objects.create(node_from=bern.node(g), node_to=geneva.node(g), 
             graph=g)
         self.assertEqual(zurich.get_ancestors(), [bern, geneva])
-    
+
     def test_counts(self):
         """
 Test counts for ancestor, descendants, etc.
         Edge.objects.create(lucern_node, bern_node)
         Edge.objects.create(geneva_node, zurich_node)
         Edge.objects.create(lucern_node, zurich_node)
-        
+
         self.assertEqual(chur.get_child_count(), 2)
         self.assertFalse(chur.is_child_node())
         self.assertFalse(chur.is_leaf_node())
         self.assertEqual(chur.get_descendant_count(), 4)
         self.assertEqual(chur.get_parent_count(), 0)
         self.assertEqual(chur.get_ancestor_count(), 0)
-        
+
         self.assertEqual(zurich.get_child_count(), 2)
         self.assertTrue(zurich.is_child_node())
         self.assertFalse(zurich.is_leaf_node())
         self.assertEqual(zurich.get_descendant_count(), 2)
         self.assertEqual(zurich.get_parent_count(), 1)
         self.assertEqual(zurich.get_ancestor_count(), 1)
-        
+
         self.assertEqual(lucern.get_child_count(), 0)
         self.assertTrue(lucern.is_child_node())
         self.assertTrue(lucern.is_leaf_node())
         self.assertEqual(lucern.get_descendant_count(), 0)
         self.assertEqual(lucern.get_parent_count(), 2)
         self.assertEqual(lucern.get_ancestor_count(), 3)
-    
+
     def test_root_nodes(self):
         """
      Zurich  Bern
         Edge.objects.create(zurich_node, lucern_node)
         self.assertEqual(set(g.root_nodes), set([bern_node, zurich_node]))
         self.assertEqual(set(g.root_node_objects), set([zurich, bern]))
-        

File django_dag/tests/database.py

 from dag.errors import *
 from django.test import TestCase
 
+
 class DatabaseTestCase(TestCase):
-
     def test_basic(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         self.assertEqual(Node.objects.all().count(), 3)
         self.assertEqual(Edge.objects.all().count(), 2)
         self.assertEqual(TransitiveClosure.objects.all().count(), 3)
-    
+
     def test_linking_same_node(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         self.assertRaises(CircularReferenceError, Edge.objects.create, n1, n1)
         self.assertRaises(CircularReferenceError, Edge.objects.create, n1, n1, 
             g)
-    
+
     def test_circular_reference(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         Edge.objects.create(n1, n2)
         Edge.objects.create(n2, n3)
         self.assertRaises(CircularReferenceError, Edge.objects.create, n3, n1)
-    
+
     def test_delete_edge(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         self.assertEqual(Node.objects.all().count(), 3)
         self.assertEqual(Edge.objects.all().count(), 1)
         self.assertEqual(TransitiveClosure.objects.all().count(), 1)
-    
+
     def test_update_edge(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         e = Edge.objects.get(node_from=n1, node_to=n2)
         e.node_to = n3
         self.assertRaises(NotImplementedError, e.save)
-    
+
     def test_add_duplicate_edge(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)
         n2 = Node.objects.create(graph=g)
-        n3 = Node.objects.create(graph=g)
         Edge.objects.create(n1, n2)
         self.assertRaises(DuplicateEdgeError, Edge.objects.create, n1, n2)
-    
+
     def test_diamond(self):
         g = Graph.objects.create(name="graph")
         n1 = Node.objects.create(graph=g)

File django_dag/tests/different_graphs.py

 from dag.errors import *
 from django.test import TestCase
 
+
 class DifferentGraphsTestCase(TestCase):
     def test_combine_graphs(self):
         g1 = Graph.objects.create(name="graph 1")
         self.assertEqual(Graph.objects.all().count(), 1)
         self.assertEqual(Edge.objects.filter(graph__id=g2_id).count(), 0)
         self.assertEqual(Node.objects.filter(graph__id=g2_id).count(), 0)
-    

File django_dag/tests/models.py

 from django.db import models
 from dag.mixins import GraphMixin
 
+
 class City(GraphMixin):
     name = models.TextField(unique=True)
 
     def __unicode__(self):
         return self.name
 
+
 class Color(GraphMixin):
     name = models.TextField(unique=True)
-    
+
     def __unicode__(self):
         return self.name

File django_dag/tests/settings.py

 DATABASES = {
-   'default' : {
-       'ENGINE' : 'django.db.backends.postgresql_psycopg2',
-       'NAME' : 'django-graph',
-       'USER' : 'django',
+   'default': {
+       'ENGINE': 'django.db.backends.postgresql_psycopg2',
+       'NAME': 'django-graph',
+       'USER': 'django',
        'PASSWORD': 'django'
    },
 }

File django_dag/utils.py

 from django.contrib.contenttypes.models import ContentType
-from django.db import connection, transaction
-from errors import CircularReferenceError, SeparateGraphsError, \
-    DuplicateEdgeError
 from sql import sql
 
 UPWARDS = """select min(tc.depth) as depth, n.id as node_id, n.content_type_id, n.object_id 
         group by n.id, n.content_type_id, n.object_id, tc.depth
         order by tc.depth"""
 
+
 def flatten(x):
     """Flattened a set of iterables into a single list.
-    
+
     From http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks"""
     result = []
     for el in x:
             result.append(el)
     return result
 
+
 def get_objects_for_node(node, max_depth=9999, downwards=True, 
         sort_reverse=False, using="default"):
     """Take a Node and an optional maximum depth, and return a list of model objects linked by generic keys and sorted by depth. 
         if obj['content_type_id']:
             content_type_dict.setdefault(obj['content_type_id'], 
                 []).append(obj['object_id']) 
-        else: # Blank content type
+        else:  # Blank content type
             blank_nodes.append((obj['node_id'], obj['depth']))
 
     # Create dictionary linking (object_id, content_type) to depth
         ))
     objs = flatten([create_objects_iterable_with_depth(c, content_type_dict, 
         depth_dict) for c in content_types])
-    
+
     # Add nodes without content types
     from models import Node
     blank_nodes_queryset = Node.objects.filter(id__in=[x[0] for x in \
     for index, node in enumerate(blank_nodes_queryset):
         node._depth = blank_nodes[index][1]
         objs.append(node)
-    
+
     objs = sort_by_attribute(objs, "_depth", sort_reverse=sort_reverse)
     return objs
 
+
 def create_objects_iterable_with_depth(content_type, content_type_dict, 
         depth_dict):
     model = content_type.model_class()
         obj._depth = depth_dict[(content_type.id, obj.id)]
     return objs
 
+
 def create_objects_iterable(content_type, content_type_dict):
     model = content_type.model_class()
     return model.objects.filter(id__in=content_type_dict[content_type.id])
 
+
 def sort_by_attribute(lst, attr, sort_reverse=False):
     """Sort a list of objects based on an attribute.
-    
+
     Falls back on list index if attributes are equal.
     Based on http://code.activestate.com/recipes/52230/"""
-    intermed = [ (getattr(obj,attr), index, obj) for index, obj in enumerate(
+    intermed = [(getattr(obj, attr), index, obj) for index, obj in enumerate(
         lst)]
-    intermed.sort(reverse = sort_reverse)
-    return [ tup[-1] for tup in intermed ]
+    intermed.sort(reverse=sort_reverse)
+    return [tup[-1] for tup in intermed]