Code review comment for lp:~thumper/launchpad/bmp-index-faster

Revision history for this message
Tim Penhey (thumper) wrote :

On Thu, 17 Jun 2010 17:07:26 you wrote:
> Review: Approve
> I notice neither DecoratedBug nor DecoratedBranch have docstrings, nor do
> they define an interface making them inscrutable. This is particularly
> confusing for DecoratedBug which provides self.tasks, self.bugtasks,
> self.bugtask, self.getBugTask(), self.default_bugtask - some of these are
> synonyms and some of them (getBugTask() and bugtask) return different
> things despite their names indicating the are synonyms.
>
> We can let this slide on this branch though since this is moving code to a
> better location, so a net win. It would be nice to merge the synonyms and
> document things though if possible.
>
> Otherwise all fine.

Thanks.

Did some docstring additions and simplified the bugtasks.

Tim

1=== modified file 'lib/lp/code/browser/decorations.py'
2--- lib/lp/code/browser/decorations.py 2010-06-17 01:16:30 +0000
3+++ lib/lp/code/browser/decorations.py 2010-06-17 05:57:00 +0000
4@@ -21,25 +21,38 @@
5
6
7 class DecoratedBug:
8- """Provide some additional attributes to a normal bug."""
9+ """Provide some cached attributes to a normal bug.
10+
11+ We provide cached properties where sensible, and override default bug
12+ behaviour where the cached properties can be used to avoid extra database
13+ queries.
14+ """
15 delegates(IBug, 'bug')
16
17 def __init__(self, bug, branch, tasks=None):
18 self.bug = bug
19 self.branch = branch
20- self.tasks = tasks
21- if self.tasks is None:
22- self.tasks = self.bug.bugtasks
23-
24- @property
25- def bugtasks(self):
26- return self.tasks
27+ if tasks is None:
28+ tasks = self.bug.bugtasks
29+ self.bugtasks = tasks
30
31 @property
32 def default_bugtask(self):
33- return self.tasks[0]
34+ """Use the first bugtask.
35+
36+ Use the cached tasks as calling default_bugtask on the bug object
37+ causes a DB query.
38+ """
39+ return self.bugtasks[0]
40
41 def getBugTask(self, target):
42+ """Get the bugtask for a specific target.
43+
44+ This method is overridden rather than letting it fall through to the
45+ underlying bug as the underlying implementation gets the bugtasks from
46+ self, which would in that case be the normal bug model object, which
47+ would then hit the database to get the tasks.
48+ """
49 # Copied from Bug.getBugTarget to avoid importing.
50 for bugtask in self.bugtasks:
51 if bugtask.target == target:
52@@ -49,6 +62,9 @@
53 @property
54 def bugtask(self):
55 """Return the bugtask for the branch project, or the default bugtask.
56+
57+ This method defers the identitication of the appropriate task to the
58+ branch target.
59 """
60 return self.branch.target.getBugTask(self)
61
62@@ -65,6 +81,13 @@
63
64 @cachedproperty
65 def linked_bugs(self):
66+ """Override the default behaviour of the branch object.
67+
68+ The default behaviour is just to get the bugs. We want to display the
69+ tasks however, and to avoid the extra database queries to get the
70+ tasks, we get them all at once, and provide decorated bugs (that have
71+ their tasks cached).
72+ """
73 bugs = defaultdict(list)
74 for bug, task in self.branch.getLinkedBugsAndTasks():
75 bugs[bug].append(task)
76@@ -74,14 +97,26 @@
77
78 @property
79 def displayname(self):
80+ """Override the default model property.
81+
82+ If left to the underlying model, it would call the bzr_identity on the
83+ underlying branch rather than the cached bzr_identity on the decorated
84+ branch. And that would cause two database queries.
85+ """
86 return self.bzr_identity
87
88 @cachedproperty
89 def bzr_identity(self):
90+ """Cache the result of the bzr identity.
91+
92+ The property is defined in the bzrIdentityMixin class. This uses the
93+ associatedProductSeries and associatedSuiteSourcePackages methods.
94+ """
95 return super(DecoratedBranch, self).bzr_identity
96
97 @cachedproperty
98 def is_series_branch(self):
99+ """A simple property to see if there are any series links."""
100 # True if linked to a product series or suite source package.
101 return (
102 len(self.associated_product_series) > 0 or
103@@ -97,21 +132,31 @@
104
105 @cachedproperty
106 def associated_product_series(self):
107+ """Cache the realized product series links."""
108 return list(self.branch.associatedProductSeries())
109
110 @cachedproperty
111 def suite_source_packages(self):
112+ """Cache the realized suite source package links."""
113 return list(self.branch.associatedSuiteSourcePackages())
114
115 @cachedproperty
116 def upgrade_pending(self):
117+ """Cache the result as the property hits the database."""
118 return self.branch.upgrade_pending
119
120 @cachedproperty
121 def subscriptions(self):
122+ """Cache the realized branch subscription objects."""
123 return list(self.branch.subscriptions)
124
125 def hasSubscription(self, user):
126+ """Override the default branch implementation.
127+
128+ The default implementation hits the database. Since we have a full
129+ list of subscribers anyway, a simple check over the list is
130+ sufficient.
131+ """
132 for sub in self.subscriptions:
133 if sub.person == user:
134 return True
135@@ -119,6 +164,11 @@
136
137 @cachedproperty
138 def latest_revisions(self):
139+ """Cache the query result.
140+
141+ When a tal:repeat is used, the method is called twice. Firstly to
142+ check that there is something to iterate over, and secondly for the
143+ actual iteration. Without the cached property, the database is hit
144+ twice.
145+ """
146 return list(self.branch.latest_revisions())
147-
148-

« Back to merge proposal