Merge ~cjwatson/launchpad:rename-conjoined-bug-tasks into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 5d65b0763acbe446c92844a7ec3b2676366d3424
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:rename-conjoined-bug-tasks
Merge into: launchpad:master
Diff against target: 1323 lines (+238/-238)
26 files modified
lib/lp/bugs/browser/bugnomination.py (+1/-1)
lib/lp/bugs/browser/bugtask.py (+14/-14)
lib/lp/bugs/configure.zcml (+3/-3)
lib/lp/bugs/doc/bug-set-status.txt (+4/-4)
lib/lp/bugs/doc/bugtask-expiration.txt (+8/-8)
lib/lp/bugs/doc/bugwatch.txt (+12/-12)
lib/lp/bugs/interfaces/bug.py (+2/-2)
lib/lp/bugs/interfaces/bugtask.py (+6/-6)
lib/lp/bugs/model/bug.py (+6/-6)
lib/lp/bugs/model/bugtask.py (+45/-45)
lib/lp/bugs/model/bugtasksearch.py (+23/-23)
lib/lp/bugs/model/bugwatch.py (+2/-2)
lib/lp/bugs/model/tests/test_bugtask.py (+23/-24)
lib/lp/bugs/model/tests/test_bugtasksearch.py (+1/-1)
lib/lp/bugs/scripts/bugexpire.py (+2/-2)
lib/lp/bugs/scripts/tests/test_bugimport.py (+1/-1)
lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt (+5/-5)
lib/lp/bugs/tests/bugtarget-questiontarget.txt (+1/-1)
lib/lp/bugs/tests/test_bugsearch_conjoined.py (+41/-41)
lib/lp/bugs/tests/test_bugwatch.py (+1/-1)
lib/lp/registry/browser/__init__.py (+2/-2)
lib/lp/registry/browser/milestone.py (+5/-5)
lib/lp/registry/model/milestone.py (+2/-2)
lib/lp/registry/model/person.py (+12/-11)
lib/lp/registry/tests/test_milestonetag.py (+1/-1)
lib/lp/registry/tests/test_person.py (+15/-15)
Reviewer Review Type Date Requested Status
Ioana Lasc Approve
Review via email: mp+414348@code.launchpad.net

Commit message

Rename conjoined master/slave bug tasks to primary/replica

Description of the change

Launchpad bugs have a peculiar feature called "conjoined tasks". For example, the tasks on a bug that's being primarily worked on in the jammy series of Ubuntu but whose fix then needs to be backported to focal and impish might look like this:

  base-files (Ubuntu) Status tracked in Jammy
   -> Focal Triaged Critical
   -> Impish Triaged Critical
   -> Jammy In Progress Critical

In this case, the task on base-files (Ubuntu) is currently referred to as a "conjoined slave", and the task on base-files (Ubuntu Jammy) is its corresponding "conjoined master". The metadata of the conjoined slave cannot be updated independently; instead, changes are automatically propagated from the conjoined master.

This terminology obviously doesn't fit with inclusive naming standards. Rename "conjoined master" to "conjoined primary" and "conjoined slave" to "conjoined replica", which I think also expresses the metadata replication relationship a little more clearly.

I also considered renaming "conjoined master" to "conjoined series-specific task" (or `conjoined_specific`) and "conjoined slave" to "conjoined generic task" (or `conjoined_generic`). That had the advantage of making it clearer which way round the tasks go (which I always find confusing and have to look up), but it's more cumbersome in prose, and it makes the replication relationship less clear.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

Conjoined primary and replica sound good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/bugs/browser/bugnomination.py b/lib/lp/bugs/browser/bugnomination.py
2index 1c2319e..40dddbd 100644
3--- a/lib/lp/bugs/browser/bugnomination.py
4+++ b/lib/lp/bugs/browser/bugnomination.py
5@@ -143,7 +143,7 @@ class BugNominationTableRowView(LaunchpadView):
6 """Browser view class for rendering a nomination table row."""
7
8 # This method will be called to render the bug nomination.
9- renderNonConjoinedSlave = LaunchpadView.__call__
10+ renderNonConjoinedReplica = LaunchpadView.__call__
11
12 def getNominationPerson(self):
13 """Return the IPerson associated with this nomination.
14diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py
15index f0197a2..1ae7e7c 100644
16--- a/lib/lp/bugs/browser/bugtask.py
17+++ b/lib/lp/bugs/browser/bugtask.py
18@@ -1937,17 +1937,17 @@ class BugTasksTableView(LaunchpadView):
19 ))
20
21 def _getTableRowView(self, context, is_converted_to_question,
22- is_conjoined_slave):
23+ is_conjoined_replica):
24 """Get the view for the context, and initialize it.
25
26- The view's is_conjoined_slave and is_converted_to_question
27+ The view's is_conjoined_replica and is_converted_to_question
28 attributes are set, as well as the edit view.
29 """
30 view = getMultiAdapter(
31 (context, self.request),
32 name='+bugtasks-and-nominations-table-row')
33 view.is_converted_to_question = is_converted_to_question
34- view.is_conjoined_slave = is_conjoined_slave
35+ view.is_conjoined_replica = is_conjoined_replica
36
37 view.edit_view = getMultiAdapter(
38 (context, self.request), name='+edit-form')
39@@ -1991,7 +1991,7 @@ class BugTasksTableView(LaunchpadView):
40 list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
41 ids, need_validity=True))
42
43- # Build a cache we can pass on to getConjoinedMaster(), so that
44+ # Build a cache we can pass on to getConjoinedPrimary(), so that
45 # it doesn't have to iterate over all the bug tasks in each loop
46 # iteration.
47 bugtasks_by_package = bug.getBugTasksByPackageName(all_bugtasks)
48@@ -2017,11 +2017,11 @@ class BugTasksTableView(LaunchpadView):
49 (parent, self.request),
50 name='+bugtasks-and-nominations-table-row'))
51
52- conjoined_master = bugtask.getConjoinedMaster(
53+ conjoined_primary = bugtask.getConjoinedPrimary(
54 all_bugtasks, bugtasks_by_package)
55 view = self._getTableRowView(
56 bugtask, is_converted_to_question,
57- conjoined_master is not None)
58+ conjoined_primary is not None)
59 bugtask_and_nomination_views.append(view)
60 target = bugtask.product or bugtask.distribution
61 if not target:
62@@ -2042,7 +2042,7 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin,
63 BugTaskPrivilegeMixin):
64 """Browser class for rendering a bugtask row on the bug page."""
65
66- is_conjoined_slave = None
67+ is_conjoined_replica = None
68 is_converted_to_question = None
69 target_link_title = None
70 many_bugtasks = False
71@@ -2074,7 +2074,7 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin,
72 # time.
73 expandable=(not self.many_bugtasks and self.canSeeTaskDetails()),
74 indent_task=ISeriesBugTarget.providedBy(self.context.target),
75- is_conjoined_slave=self.is_conjoined_slave,
76+ is_conjoined_replica=self.is_conjoined_replica,
77 task_link=task_link,
78 edit_link=edit_link,
79 can_edit=can_edit,
80@@ -2112,13 +2112,13 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin,
81 It is independent of whether they can *change* the status; you
82 need to expand the details to see any milestone set.
83 """
84- assert self.is_conjoined_slave is not None, (
85- 'is_conjoined_slave should be set before rendering the page.')
86+ assert self.is_conjoined_replica is not None, (
87+ 'is_conjoined_replica should be set before rendering the page.')
88 assert self.is_converted_to_question is not None, (
89 'is_converted_to_question should be set before rendering the'
90 ' page.')
91 return (self.displayEditForm() and
92- not self.is_conjoined_slave and
93+ not self.is_conjoined_replica and
94 self.context.bug.duplicateof is None and
95 not self.is_converted_to_question)
96
97@@ -2133,9 +2133,9 @@ class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin,
98 """Get the series to which this task is targeted."""
99 return self._getSeriesTargetNameHelper(self.context)
100
101- def getConjoinedMasterName(self):
102- """Get the conjoined master's name for displaying."""
103- return self._getSeriesTargetNameHelper(self.context.conjoined_master)
104+ def getConjoinedPrimaryName(self):
105+ """Get the conjoined primary's name for displaying."""
106+ return self._getSeriesTargetNameHelper(self.context.conjoined_primary)
107
108 @property
109 def bugtask_icon(self):
110diff --git a/lib/lp/bugs/configure.zcml b/lib/lp/bugs/configure.zcml
111index b2a54ac..5b46af7 100644
112--- a/lib/lp/bugs/configure.zcml
113+++ b/lib/lp/bugs/configure.zcml
114@@ -226,10 +226,10 @@
115 getDelta
116 pillar
117 bugtask_branches
118- conjoined_master
119- conjoined_slave
120+ conjoined_primary
121+ conjoined_replica
122 subscribe
123- getConjoinedMaster
124+ getConjoinedPrimary
125 findSimilarBugs
126 getContributorInfo"/>
127 <require
128diff --git a/lib/lp/bugs/doc/bug-set-status.txt b/lib/lp/bugs/doc/bug-set-status.txt
129index 4c4f033..f89dee0 100644
130--- a/lib/lp/bugs/doc/bug-set-status.txt
131+++ b/lib/lp/bugs/doc/bug-set-status.txt
132@@ -109,14 +109,14 @@ is edited.
133 >>> firefox_trunk_bugtask.status.name
134 'INCOMPLETE'
135
136-If the target bugtask has a conjoined master bugtask, the conjoined
137-master will be edited and returned. The conjoined slave is of course
138+If the target bugtask has a conjoined primary bugtask, the conjoined
139+primary will be edited and returned. The conjoined replica is of course
140 updated automatically.
141
142- >>> firefox_bugtask = firefox_trunk_bugtask.conjoined_slave
143+ >>> firefox_bugtask = firefox_trunk_bugtask.conjoined_replica
144 >>> print(firefox_bugtask.target.name)
145 firefox
146- >>> firefox_bugtask.conjoined_master is not None
147+ >>> firefox_bugtask.conjoined_primary is not None
148 True
149 >>> firefox_bugtask.status.name
150 'INCOMPLETE'
151diff --git a/lib/lp/bugs/doc/bugtask-expiration.txt b/lib/lp/bugs/doc/bugtask-expiration.txt
152index f34bb17..9478b1c 100644
153--- a/lib/lp/bugs/doc/bugtask-expiration.txt
154+++ b/lib/lp/bugs/doc/bugtask-expiration.txt
155@@ -91,7 +91,7 @@ that no bug tasks can be expired.
156 ... 'test@canonical.com')
157
158 # A expirable bugtask. It will be expired because its conjoined
159- # master can be expired.
160+ # primary can be expired.
161 >>> from lp.bugs.tests.bug import create_old_bug
162 >>> ubuntu_bugtask = create_old_bug('expirable_distro', 351, ubuntu)
163 >>> ubuntu_bugtask.bug.permits_expiration
164@@ -100,10 +100,10 @@ that no bug tasks can be expired.
165 True
166
167 # An expirable bugtask, a distroseries. The ubuntu bugtask is its
168- # conjoined slave.
169+ # conjoined replica.
170 >>> hoary_bugtask = bugtaskset.createTask(
171 ... ubuntu_bugtask.bug, sample_person, ubuntu.currentseries)
172- >>> ubuntu_bugtask.conjoined_master == hoary_bugtask
173+ >>> ubuntu_bugtask.conjoined_primary == hoary_bugtask
174 True
175 >>> ubuntu_bugtask.bug.permits_expiration
176 True
177@@ -318,13 +318,13 @@ will be expirable.
178 ubuntu True 351 Incomplete False False False False
179 hoary True 351 Incomplete False False False False
180
181-The ubuntu bugtask is never returned; it is a conjoined slave to the
182-hoary bugtask. Slave bugtasks cannot be directly expired, so they are
183+The ubuntu bugtask is never returned; it is a conjoined replica to the
184+hoary bugtask. Replica bugtasks cannot be directly expired, so they are
185 not returned by findExpirableBugTasks().
186
187 >>> ubuntu_bugtask.status.title
188 'Incomplete'
189- >>> ubuntu_bugtask.conjoined_master == hoary_bugtask
190+ >>> ubuntu_bugtask.conjoined_primary == hoary_bugtask
191 True
192
193 Reducing the age to 60 days old, both hoary and jokosher bugtasks
194@@ -531,7 +531,7 @@ After the script has run
195
196 There are three Expired bugtasks. Jokosher, hoary and ubuntu were
197 expired by the expiration process. Although ubuntu was never returned
198-by findExpirableBugTasks(), it was expired because its master (hoary)
199+by findExpirableBugTasks(), it was expired because its primary (hoary)
200 was expired. The remaining bugtasks are unchanged.
201
202 >>> summarize_bugtasks(bugtasks)
203@@ -551,7 +551,7 @@ was expired. The remaining bugtasks are unchanged.
204
205 The message explaining the reason for the expiration was posted by the
206 Launchpad Janitor celebrity. Only one message was created for when the
207-master and slave bugtasks were expired.
208+primary and replica bugtasks were expired.
209
210 >>> starting_bug_messages_count
211 2
212diff --git a/lib/lp/bugs/doc/bugwatch.txt b/lib/lp/bugs/doc/bugwatch.txt
213index 17e6f6a..4070e12 100644
214--- a/lib/lp/bugs/doc/bugwatch.txt
215+++ b/lib/lp/bugs/doc/bugwatch.txt
216@@ -417,15 +417,15 @@ The Bug Watch Updater can transition a bug to any status or importance:
217 ... debian_bugwatch.updateImportance(u'nothing', importance)
218
219
220-BugWatches against BugTasks with conjoined masters
221---------------------------------------------------
222+BugWatches against BugTasks with conjoined primaries
223+----------------------------------------------------
224
225-A conjoined bugtask involves a master and slave in in a conjoined
226-relationship. The slave is a generic product or distribution task; the
227-master is a series-specific task. If a BugWatch is linked to a BugTask
228-with a conjoined master, that bug task will not be updated when the
229+A conjoined bugtask involves a primary and replica in a conjoined
230+relationship. The replica is a generic product or distribution task; the
231+primary is a series-specific task. If a BugWatch is linked to a BugTask
232+with a conjoined primary, that bug task will not be updated when the
233 BugWatch's status or importance are updated. We can demonstrate this by
234-creating a bug task with a conjoined master.
235+creating a bug task with a conjoined primary.
236
237 >>> from zope.component import getUtility
238 >>> from lp.services.database.sqlbase import flush_database_updates
239@@ -441,15 +441,15 @@ creating a bug task with a conjoined master.
240 >>> firefox = ubuntu.getSourcePackage('mozilla-firefox')
241 >>> bug = firefox.createBug(CreateBugParams(
242 ... owner=sample_person, title='Yet another test bug',
243- ... comment="A sample bug for conjoined master tests."))
244+ ... comment="A sample bug for conjoined primary tests."))
245
246 >>> targeted_bugtask = getUtility(IBugTaskSet).createTask(
247 ... bug, sample_person, firefox.development_version)
248
249- >>> targeted_bugtask.conjoined_master is None
250+ >>> targeted_bugtask.conjoined_primary is None
251 True
252
253- >>> targeted_bugtask.conjoined_slave == bug.bugtasks[0]
254+ >>> targeted_bugtask.conjoined_replica == bug.bugtasks[0]
255 True
256
257 We use ensureBugTracker() to populate in the parameters that we don't
258@@ -467,8 +467,8 @@ specifiy, such as the bug tracker's name.
259 Now that we have our conjoined bug tasks we can use a test
260 implementation of the Roundup ExternalBugTracker to try and update
261 them. In fact, updating the bug watch will do nothing to the bug task to
262-which it is linked since that bug task is a conjoined slave. Conjoined
263-slaves must be updated through their conjoined master.
264+which it is linked since that bug task is a conjoined replica. Conjoined
265+replicas must be updated through their conjoined primary.
266
267 >>> bug.bugtasks[0].status.title
268 'New'
269diff --git a/lib/lp/bugs/interfaces/bug.py b/lib/lp/bugs/interfaces/bug.py
270index 057b9eb..db94b63 100644
271--- a/lib/lp/bugs/interfaces/bug.py
272+++ b/lib/lp/bugs/interfaces/bug.py
273@@ -578,7 +578,7 @@ class IBugView(Interface):
274 """Return a mapping from `ISourcePackageName` to its bug tasks.
275
276 This mapping is suitable to pass as the bugtasks_by_package
277- cache to getConjoinedMaster().
278+ cache to getConjoinedPrimary().
279
280 The mapping is from a `ISourcePackageName` to all the bug tasks
281 that are targeted to such a package name, no matter which
282@@ -750,7 +750,7 @@ class IBugAppend(Interface):
283 to questions. This is also true for bugs that are being developed.
284
285 The `IQuestionTarget` is provided by the `IBugTask` that is not
286- Invalid and is not a conjoined slave. Only one question can be
287+ Invalid and is not a conjoined replica. Only one question can be
288 made from a bug.
289
290 An AssertionError is raised if the bug has zero or many BugTasks
291diff --git a/lib/lp/bugs/interfaces/bugtask.py b/lib/lp/bugs/interfaces/bugtask.py
292index 141034f..5d22a40 100644
293--- a/lib/lp/bugs/interfaces/bugtask.py
294+++ b/lib/lp/bugs/interfaces/bugtask.py
295@@ -575,9 +575,9 @@ class IBugTask(IHasBug, IBugTaskDelete):
296 title=_("A list of IPersons subscribed to the bug, whether directly "
297 "or indirectly."), readonly=True)
298
299- conjoined_master = Attribute(
300+ conjoined_primary = Attribute(
301 "The series-specific bugtask in a conjoined relationship")
302- conjoined_slave = Attribute(
303+ conjoined_replica = Attribute(
304 "The generic bugtask in a conjoined relationship")
305
306 is_complete = exported(
307@@ -614,18 +614,18 @@ class IBugTask(IHasBug, IBugTaskDelete):
308 calling context does not have access to the person or pillar names.
309 """
310
311- def getConjoinedMaster(bugtasks, bugtasks_by_package=None):
312- """Return the conjoined master in the given bugtasks, if any.
313+ def getConjoinedPrimary(bugtasks, bugtasks_by_package=None):
314+ """Return the conjoined primary in the given bugtasks, if any.
315
316 :param bugtasks: The bugtasks to be considered when looking for
317- the conjoined master.
318+ the conjoined primary.
319 :param bugtasks_by_package: A cache, mapping a
320 `ISourcePackageName` to a list of bug tasks targeted to such
321 a package name. Both distribution and distro series tasks
322 should be included in this list.
323
324 This method exists mainly to allow calculating the conjoined
325- master from a cached list of bug tasks, reducing the number of
326+ primary from a cached list of bug tasks, reducing the number of
327 db queries needed.
328 """
329
330diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
331index 0a3ff8c..f7c588c 100644
332--- a/lib/lp/bugs/model/bug.py
333+++ b/lib/lp/bugs/model/bug.py
334@@ -1484,18 +1484,18 @@ class Bug(SQLBase, InformationTypeMixin):
335
336 The bugtask is selected by these rules:
337 1. It's status is not Invalid.
338- 2. It is not a conjoined slave.
339+ 2. It is not a conjoined replica.
340 Only one bugtask must meet both conditions to be return. When
341 zero or many bugtasks match, None is returned.
342 """
343- # We may want to removed the bugtask.conjoined_master check
344+ # We may want to removed the bugtask.conjoined_primary check
345 # below. It is used to simplify the task of converting
346- # conjoined bugtasks to question--since slaves cannot be
347+ # conjoined bugtasks to question--since replicas cannot be
348 # directly updated anyway.
349 non_invalid_bugtasks = [
350 bugtask for bugtask in self.bugtasks
351 if (bugtask.status != BugTaskStatus.INVALID
352- and bugtask.conjoined_master is None)]
353+ and bugtask.conjoined_primary is None)]
354 if len(non_invalid_bugtasks) != 1:
355 return None
356 [valid_bugtask] = non_invalid_bugtasks
357@@ -1755,8 +1755,8 @@ class Bug(SQLBase, InformationTypeMixin):
358 if bugtask is None:
359 return None
360
361- if bugtask.conjoined_master is not None:
362- bugtask = bugtask.conjoined_master
363+ if bugtask.conjoined_primary is not None:
364+ bugtask = bugtask.conjoined_primary
365
366 if bugtask.status == status:
367 return None
368diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
369index 2cd2b35..7b31d6d 100644
370--- a/lib/lp/bugs/model/bugtask.py
371+++ b/lib/lp/bugs/model/bugtask.py
372@@ -281,25 +281,25 @@ class PassthroughValue:
373
374 @block_implicit_flushes
375 def validate_conjoined_attribute(self, attr, value):
376- # If this is a conjoined slave then call setattr on the master.
377- # Effectively this means that making a change to the slave will
378- # actually make the change to the master (which will then be passed
379- # down to the slave, of course). This helps to prevent OOPSes when
380- # people try to update the conjoined slave via the API.
381+ # If this is a conjoined replica then call setattr on the primary.
382+ # Effectively this means that making a change to the replica will
383+ # actually make the change to the primary (which will then be passed
384+ # down to the replica, of course). This helps to prevent OOPSes when
385+ # people try to update the conjoined replica via the API.
386
387 # If the value has been wrapped in a _PassthroughValue instance,
388- # then we are being updated by our conjoined master: pass the
389+ # then we are being updated by our conjoined primary: pass the
390 # value through without any checking.
391 if isinstance(value, PassthroughValue):
392 return value.value
393
394- conjoined_master = self.conjoined_master
395- if conjoined_master is not None:
396- setattr(conjoined_master, attr, value)
397+ conjoined_primary = self.conjoined_primary
398+ if conjoined_primary is not None:
399+ setattr(conjoined_primary, attr, value)
400 return value
401
402- # If there is a conjoined slave, update that.
403- conjoined_bugtask = self.conjoined_slave
404+ # If there is a conjoined replica, update that.
405+ conjoined_bugtask = self.conjoined_replica
406 if conjoined_bugtask:
407 setattr(conjoined_bugtask, attr, PassthroughValue(value))
408
409@@ -727,26 +727,26 @@ class BugTask(StormBase):
410 result['pillar_name'] = self.pillar.displayname
411 return result
412
413- def getConjoinedMaster(self, bugtasks, bugtasks_by_package=None):
414+ def getConjoinedPrimary(self, bugtasks, bugtasks_by_package=None):
415 """See `IBugTask`."""
416- conjoined_master = None
417+ conjoined_primary = None
418 if self.distribution:
419 if bugtasks_by_package is None:
420 bugtasks_by_package = (
421 self.bug.getBugTasksByPackageName(bugtasks))
422 bugtasks = bugtasks_by_package[self.sourcepackagename]
423- possible_masters = [
424+ possible_primaries = [
425 bugtask for bugtask in bugtasks
426 if (bugtask.distroseries is not None and
427 bugtask.sourcepackagename == self.sourcepackagename)]
428 # Return early, so that we don't have to get currentseries,
429 # which is expensive.
430- if len(possible_masters) == 0:
431+ if len(possible_primaries) == 0:
432 return None
433 current_series = self.distribution.currentseries
434- for bugtask in possible_masters:
435+ for bugtask in possible_primaries:
436 if bugtask.distroseries == current_series:
437- conjoined_master = bugtask
438+ conjoined_primary = bugtask
439 break
440 elif self.product:
441 assert self.product.development_focusID is not None, (
442@@ -754,26 +754,26 @@ class BugTask(StormBase):
443 devel_focusID = self.product.development_focusID
444 for bugtask in bugtasks:
445 if bugtask.productseries_id == devel_focusID:
446- conjoined_master = bugtask
447+ conjoined_primary = bugtask
448 break
449
450- if (conjoined_master is not None and
451- conjoined_master.status in self._NON_CONJOINED_STATUSES):
452- conjoined_master = None
453- return conjoined_master
454+ if (conjoined_primary is not None and
455+ conjoined_primary.status in self._NON_CONJOINED_STATUSES):
456+ conjoined_primary = None
457+ return conjoined_primary
458
459 def _get_shortlisted_bugtasks(self):
460 return shortlist(self.bug.bugtasks, longest_expected=200)
461
462 @property
463- def conjoined_master(self):
464+ def conjoined_primary(self):
465 """See `IBugTask`."""
466- return self.getConjoinedMaster(self._get_shortlisted_bugtasks())
467+ return self.getConjoinedPrimary(self._get_shortlisted_bugtasks())
468
469 @property
470- def conjoined_slave(self):
471+ def conjoined_replica(self):
472 """See `IBugTask`."""
473- conjoined_slave = None
474+ conjoined_replica = None
475 if self.distroseries:
476 distribution = self.distroseries.distribution
477 if self.distroseries != distribution.currentseries:
478@@ -782,7 +782,7 @@ class BugTask(StormBase):
479 for bugtask in self._get_shortlisted_bugtasks():
480 if (bugtask.distribution == distribution and
481 bugtask.sourcepackagename == self.sourcepackagename):
482- conjoined_slave = bugtask
483+ conjoined_replica = bugtask
484 break
485 elif self.productseries:
486 product = self.productseries.product
487@@ -791,29 +791,29 @@ class BugTask(StormBase):
488 return None
489 for bugtask in self._get_shortlisted_bugtasks():
490 if bugtask.product == product:
491- conjoined_slave = bugtask
492+ conjoined_replica = bugtask
493 break
494
495- if (conjoined_slave is not None and
496+ if (conjoined_replica is not None and
497 self.status in self._NON_CONJOINED_STATUSES):
498- conjoined_slave = None
499- return conjoined_slave
500+ conjoined_replica = None
501+ return conjoined_replica
502
503- def _syncFromConjoinedSlave(self):
504- """Ensure the conjoined master is synched from its slave.
505+ def _syncFromConjoinedReplica(self):
506+ """Ensure the conjoined primary is synched from its replica.
507
508 This method should be used only directly after when the
509- conjoined master has been created after the slave, to ensure
510+ conjoined primary has been created after the replica, to ensure
511 that they are in sync from the beginning.
512 """
513- conjoined_slave = self.conjoined_slave
514+ conjoined_replica = self.conjoined_replica
515
516 for synched_attr in self._CONJOINED_ATTRIBUTES:
517- slave_attr_value = getattr(conjoined_slave, synched_attr)
518+ replica_attr_value = getattr(conjoined_replica, synched_attr)
519 # Bypass our checks that prevent setting attributes on
520- # conjoined masters by calling the underlying sqlobject
521+ # conjoined primaries by calling the underlying sqlobject
522 # setter methods directly.
523- setattr(self, synched_attr, PassthroughValue(slave_attr_value))
524+ setattr(self, synched_attr, PassthroughValue(replica_attr_value))
525
526 def transitionToMilestone(self, new_milestone, user):
527 """See `IBugTask`."""
528@@ -1643,8 +1643,8 @@ class BugTaskSet:
529 del get_property_cache(bug).bugtasks
530 for bugtask in tasks:
531 bugtask.updateTargetNameCache()
532- if bugtask.conjoined_slave:
533- bugtask._syncFromConjoinedSlave()
534+ if bugtask.conjoined_replica:
535+ bugtask._syncFromConjoinedReplica()
536 else:
537 # Set date_* properties, if we're not conjoined.
538 bugtask._setStatusDateProperties(
539@@ -1739,11 +1739,11 @@ class BugTaskSet:
540 Bugtasks cannot transition to Invalid automatically unless they meet
541 all the rules stated above.
542
543- This implementation returns the master of the master-slave conjoined
544- pairs of bugtasks. Slave conjoined bugtasks are not included in the
545- list because they can only be expired by calling the master bugtask's
546- transitionToStatus() method. See 'Conjoined Bug Tasks' in
547- c.l.doc/bugtasks.txt.
548+ This implementation returns the primary of the primary-replica
549+ conjoined pairs of bugtasks. Replica conjoined bugtasks are not
550+ included in the list because they can only be expired by calling the
551+ primary bugtask's transitionToStatus() method. See
552+ lp.bugs.model.tests.test_bugtask.TestConjoinedBugTasks.
553
554 Only bugtasks the specified user has permission to view are
555 returned. The Janitor celebrity has permission to view all bugs.
556diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py
557index 1bf2eed..03b306f 100644
558--- a/lib/lp/bugs/model/bugtasksearch.py
559+++ b/lib/lp/bugs/model/bugtasksearch.py
560@@ -931,38 +931,38 @@ def _build_status_clause(col, status):
561
562
563 def _build_exclude_conjoined_clause(milestone):
564- """Exclude bugtasks with a conjoined master.
565+ """Exclude bugtasks with a conjoined primary.
566
567 This search option only makes sense when searching for bugtasks
568 for a milestone. Only bugtasks for a project or a distribution
569- can have a conjoined master bugtask, which is a bugtask on the
570+ can have a conjoined primary bugtask, which is a bugtask on the
571 project's development focus series or the distribution's
572 currentseries. The project bugtask or the distribution bugtask
573- will always have the same milestone set as its conjoined master
574+ will always have the same milestone set as its conjoined primary
575 bugtask, if it exists on the bug. Therefore, this prevents a lot
576 of bugs having two bugtasks listed in the results. However, it
577 is ok if a bug has multiple bugtasks in the results as long as
578 those other bugtasks are on other series.
579 """
580 # XXX: EdwinGrubbs 2010-12-15 bug=682989
581- # (ConjoinedMaster.bug == X) produces the wrong sql, but
582- # (ConjoinedMaster.bugID == X) works right. This bug applies to
583+ # (ConjoinedPrimary.bug == X) produces the wrong sql, but
584+ # (ConjoinedPrimary.bugID == X) works right. This bug applies to
585 # all foreign keys on the ClassAlias.
586
587- # Perform a LEFT JOIN to the conjoined master bugtask. If the
588- # conjoined master is not null, it gets filtered out.
589- ConjoinedMaster = ClassAlias(BugTask, 'ConjoinedMaster')
590- extra_clauses = [ConjoinedMaster.id == None]
591+ # Perform a LEFT JOIN to the conjoined primary bugtask. If the
592+ # conjoined primary is not null, it gets filtered out.
593+ ConjoinedPrimary = ClassAlias(BugTask, 'ConjoinedPrimary')
594+ extra_clauses = [ConjoinedPrimary.id == None]
595 if milestone.distribution is not None:
596 current_series = milestone.distribution.currentseries
597 join = LeftJoin(
598- ConjoinedMaster,
599- And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id,
600+ ConjoinedPrimary,
601+ And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id,
602 BugTaskFlat.distribution_id == milestone.distribution.id,
603- ConjoinedMaster.distroseries_id == current_series.id,
604- Not(ConjoinedMaster._status.is_in(
605+ ConjoinedPrimary.distroseries_id == current_series.id,
606+ Not(ConjoinedPrimary._status.is_in(
607 BugTask._NON_CONJOINED_STATUSES))))
608- join_tables = [(ConjoinedMaster, join)]
609+ join_tables = [(ConjoinedPrimary, join)]
610 else:
611 if IProjectGroupMilestone.providedBy(milestone):
612 # Since an IProjectGroupMilestone could have bugs with
613@@ -973,11 +973,11 @@ def _build_exclude_conjoined_clause(milestone):
614 Join(Milestone, BugTaskFlat.milestone_id == Milestone.id),
615 LeftJoin(Product, BugTaskFlat.product_id == Product.id),
616 LeftJoin(
617- ConjoinedMaster,
618- And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id,
619- ConjoinedMaster.productseries_id
620+ ConjoinedPrimary,
621+ And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id,
622+ ConjoinedPrimary.productseries_id
623 == Product.development_focusID,
624- Not(ConjoinedMaster._status.is_in(
625+ Not(ConjoinedPrimary._status.is_in(
626 BugTask._NON_CONJOINED_STATUSES)))),
627 ]
628 # join.right is the table name.
629@@ -986,13 +986,13 @@ def _build_exclude_conjoined_clause(milestone):
630 dev_focus_id = (
631 milestone.product.development_focusID)
632 join = LeftJoin(
633- ConjoinedMaster,
634- And(ConjoinedMaster.bug_id == BugTaskFlat.bug_id,
635+ ConjoinedPrimary,
636+ And(ConjoinedPrimary.bug_id == BugTaskFlat.bug_id,
637 BugTaskFlat.product_id == milestone.product.id,
638- ConjoinedMaster.productseries_id == dev_focus_id,
639- Not(ConjoinedMaster._status.is_in(
640+ ConjoinedPrimary.productseries_id == dev_focus_id,
641+ Not(ConjoinedPrimary._status.is_in(
642 BugTask._NON_CONJOINED_STATUSES))))
643- join_tables = [(ConjoinedMaster, join)]
644+ join_tables = [(ConjoinedPrimary, join)]
645 else:
646 raise AssertionError(
647 "A milestone must always have either a project, "
648diff --git a/lib/lp/bugs/model/bugwatch.py b/lib/lp/bugs/model/bugwatch.py
649index e9dc359..8b564a3 100644
650--- a/lib/lp/bugs/model/bugwatch.py
651+++ b/lib/lp/bugs/model/bugwatch.py
652@@ -148,8 +148,8 @@ class BugWatch(SQLBase):
653 """Yield the bug tasks that are eligible for update."""
654 for bugtask in self.bugtasks:
655 # We don't update conjoined bug tasks; they must be
656- # updated through their conjoined masters.
657- if bugtask.conjoined_master is not None:
658+ # updated through their conjoined primaries.
659+ if bugtask.conjoined_primary is not None:
660 continue
661 # We don't update tasks of duplicate bugs.
662 if bugtask.bug.duplicateof is not None:
663diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py
664index f50033f..4089d86 100644
665--- a/lib/lp/bugs/model/tests/test_bugtask.py
666+++ b/lib/lp/bugs/model/tests/test_bugtask.py
667@@ -1685,10 +1685,10 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
668 return BugData(owner, distro, distro_release, source_package, bug,
669 generic_task, series_task)
670
671- def test_editing_generic_status_reflects_upon_conjoined_master(self):
672- # If a change is made to the status of a conjoined slave
673+ def test_editing_generic_status_reflects_upon_conjoined_primary(self):
674+ # If a change is made to the status of a conjoined replica
675 # (generic) task, that change is reflected upon the conjoined
676- # master.
677+ # primary.
678 data = self._setupBugData()
679 with person_logged_in(data.owner):
680 # Both the generic task and the series task start off with the
681@@ -1704,10 +1704,10 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
682 self.assertEqual(BugTaskStatus.CONFIRMED,
683 data.series_task.status)
684
685- def test_editing_generic_importance_reflects_upon_conjoined_master(self):
686- # If a change is made to the importance of a conjoined slave
687+ def test_editing_generic_importance_reflects_upon_conjoined_primary(self):
688+ # If a change is made to the importance of a conjoined replica
689 # (generic) task, that change is reflected upon the conjoined
690- # master.
691+ # primary.
692 data = self._setupBugData()
693 with person_logged_in(data.owner):
694 data.generic_task.transitionToImportance(BugTaskImportance.HIGH,
695@@ -1715,19 +1715,19 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
696 self.assertEqual(BugTaskImportance.HIGH,
697 data.series_task.importance)
698
699- def test_editing_generic_assignee_reflects_upon_conjoined_master(self):
700- # If a change is made to the assignee of a conjoined slave
701+ def test_editing_generic_assignee_reflects_upon_conjoined_primary(self):
702+ # If a change is made to the assignee of a conjoined replica
703 # (generic) task, that change is reflected upon the conjoined
704- # master.
705+ # primary.
706 data = self._setupBugData()
707 with person_logged_in(data.owner):
708 data.generic_task.transitionToAssignee(data.owner)
709 self.assertEqual(data.owner, data.series_task.assignee)
710
711- def test_editing_generic_package_reflects_upon_conjoined_master(self):
712- # If a change is made to the source package of a conjoined slave
713+ def test_editing_generic_package_reflects_upon_conjoined_primary(self):
714+ # If a change is made to the source package of a conjoined replica
715 # (generic) task, that change is reflected upon the conjoined
716- # master.
717+ # primary.
718 data = self._setupBugData()
719 source_package_name = self.factory.makeSourcePackageName("ham")
720 self.factory.makeSourcePackagePublishingHistory(
721@@ -1777,7 +1777,7 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
722 self.assertEqual(con_devel_task.milestone.name, 'test')
723
724 def test_non_current_dev_lacks_conjoined(self):
725- """Tasks not the current dev focus lacks conjoined masters or slaves.
726+ """Tasks not the current dev focus lack conjoined primaries/replicas.
727 """
728 # Only owners, experts, or admins can create a series.
729 login('foo.bar@canonical.com')
730@@ -1801,8 +1801,8 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
731
732 stable_netapplet_task = getUtility(IBugTaskSet).createTask(
733 ubuntu_netapplet_bug, launchbag.user, alsa_utils_stable)
734- self.assertIsNone(stable_netapplet_task.conjoined_master)
735- self.assertIsNone(stable_netapplet_task.conjoined_slave)
736+ self.assertIsNone(stable_netapplet_task.conjoined_primary)
737+ self.assertIsNone(stable_netapplet_task.conjoined_replica)
738
739 warty = ubuntu.getSeries('warty')
740 self.assertNotEqual(warty, ubuntu.currentseries)
741@@ -1811,12 +1811,11 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
742 ubuntu_netapplet_bug, launchbag.user,
743 warty.getSourcePackage(ubuntu_netapplet.sourcepackagename))
744
745- self.assertIsNone(warty_netapplet_task.conjoined_master)
746- self.assertIsNone(warty_netapplet_task.conjoined_slave)
747+ self.assertIsNone(warty_netapplet_task.conjoined_primary)
748+ self.assertIsNone(warty_netapplet_task.conjoined_replica)
749
750 def test_no_conjoined_without_current_series(self):
751- """Distributions without current series lack a conjoined master/slave.
752- """
753+ """Distros without current series lack a conjoined primary/replica."""
754 login('foo.bar@canonical.com')
755 launchbag = getUtility(ILaunchBag)
756 ubuntu = getUtility(IDistributionSet).get(1)
757@@ -1832,13 +1831,13 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
758 gentoo_netapplet_task = getUtility(IBugTaskSet).createTask(
759 ubuntu_netapplet_bug, launchbag.user,
760 gentoo.getSourcePackage(ubuntu_netapplet.sourcepackagename))
761- self.assertIsNone(gentoo_netapplet_task.conjoined_master)
762- self.assertIsNone(gentoo_netapplet_task.conjoined_slave)
763+ self.assertIsNone(gentoo_netapplet_task.conjoined_primary)
764+ self.assertIsNone(gentoo_netapplet_task.conjoined_replica)
765
766 def test_conjoined_broken_relationship(self):
767 """A conjoined relationship can be broken, though.
768
769- If the development task (i.e the conjoined master) is Won't Fix, it
770+ If the development task (i.e the conjoined primary) is Won't Fix, it
771 means that the bug is deferred to the next series. In this case the
772 development task should be Won't Fix, while the generic task keeps the
773 value it had before, allowing it to stay open.
774@@ -1874,8 +1873,8 @@ class TestConjoinedBugTasks(TestCaseWithFactory):
775 self.assertIsNotNone(current_series_netapplet_task.date_closed)
776
777 # And the bugtasks are no longer conjoined:
778- self.assertIsNone(generic_netapplet_task.conjoined_master)
779- self.assertIsNone(current_series_netapplet_task.conjoined_slave)
780+ self.assertIsNone(generic_netapplet_task.conjoined_primary)
781+ self.assertIsNone(current_series_netapplet_task.conjoined_replica)
782
783 # If the current development release is marked as Invalid, then the
784 # bug is invalid for all future series too, and so the general bugtask
785diff --git a/lib/lp/bugs/model/tests/test_bugtasksearch.py b/lib/lp/bugs/model/tests/test_bugtasksearch.py
786index 8c9d81c..a8bd7a7 100644
787--- a/lib/lp/bugs/model/tests/test_bugtasksearch.py
788+++ b/lib/lp/bugs/model/tests/test_bugtasksearch.py
789@@ -2272,7 +2272,7 @@ class TestBugTaskSearch(TestCaseWithFactory):
790 # on the bug that would normally trigger lazy evaluation for security
791 # checking. Note that the 'id' attribute does not trigger a check.
792 with StormStatementRecorder() as recorder:
793- [task.getConjoinedMaster for task in tasks]
794+ [task.getConjoinedPrimary for task in tasks]
795 self.assertThat(recorder, has_expected_queries)
796
797 def test_omit_targeted_default_is_false(self):
798diff --git a/lib/lp/bugs/scripts/bugexpire.py b/lib/lp/bugs/scripts/bugexpire.py
799index 89d656e..2d4e090 100644
800--- a/lib/lp/bugs/scripts/bugexpire.py
801+++ b/lib/lp/bugs/scripts/bugexpire.py
802@@ -78,8 +78,8 @@ class BugJanitor:
803 self.log.info(
804 'Found %d bugtasks to expire.' % incomplete_bugtasks.count())
805 for bugtask in incomplete_bugtasks:
806- # We don't expire bugtasks with conjoined masters.
807- if bugtask.conjoined_master:
808+ # We don't expire bugtasks with conjoined primaries.
809+ if bugtask.conjoined_primary:
810 continue
811
812 with notify_modified(bugtask, ['status'], user=self.janitor):
813diff --git a/lib/lp/bugs/scripts/tests/test_bugimport.py b/lib/lp/bugs/scripts/tests/test_bugimport.py
814index f4f5f7e..35d1196 100644
815--- a/lib/lp/bugs/scripts/tests/test_bugimport.py
816+++ b/lib/lp/bugs/scripts/tests/test_bugimport.py
817@@ -826,7 +826,7 @@ class TestBugWatch:
818 def updateStatus(self, new_remote_status, new_malone_status):
819 """See `IBugWatch`."""
820 for bugtask in self.bug.bugtasks:
821- if bugtask.conjoined_master is not None:
822+ if bugtask.conjoined_primary is not None:
823 continue
824 bugtask = removeSecurityProxy(bugtask)
825 bugtask._status = new_malone_status
826diff --git a/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt b/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt
827index 61e716f..4992385 100644
828--- a/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt
829+++ b/lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt
830@@ -12,7 +12,7 @@
831 <td style="padding: 0.3em 0em 0.3em 1.5em"
832 tal:condition="data/indent_task">
833 <span class="sprite milestone"></span>
834- <tal:not-conjoined-task condition="not: data/is_conjoined_slave">
835+ <tal:not-conjoined-task condition="not: data/is_conjoined_replica">
836 <a
837 tal:attributes="href data/target_link"
838 tal:content="view/getSeriesTargetName"
839@@ -46,18 +46,18 @@
840 </span>
841 </td>
842
843- <tal:conjoined-task condition="data/is_conjoined_slave">
844+ <tal:conjoined-task condition="data/is_conjoined_replica">
845 <td colspan="5" style="vertical-align: middle">
846 <span class="lesser">
847 Status tracked in
848- <tal:master tal:replace="view/getConjoinedMasterName">
849+ <tal:primary tal:replace="view/getConjoinedPrimaryName">
850 Hoary
851- </tal:master>
852+ </tal:primary>
853 </span>
854 </td>
855 </tal:conjoined-task>
856
857- <tal:not-conjoined-task condition="not:data/is_conjoined_slave">
858+ <tal:not-conjoined-task condition="not:data/is_conjoined_replica">
859 <td style="width: 20%; vertical-align: middle">
860 <div class="status-content"
861 style="width: 100%; float: left"
862diff --git a/lib/lp/bugs/tests/bugtarget-questiontarget.txt b/lib/lp/bugs/tests/bugtarget-questiontarget.txt
863index 5a7ae5b..4643a0a 100644
864--- a/lib/lp/bugs/tests/bugtarget-questiontarget.txt
865+++ b/lib/lp/bugs/tests/bugtarget-questiontarget.txt
866@@ -205,7 +205,7 @@ provided
867
868 >>> evo_bugtask.transitionToStatus(BugTaskStatus.INVALID, sample_person)
869 >>> len([bt for bt in bugtasks
870- ... if bt.status.title == 'New' and bt.conjoined_master is None])
871+ ... if bt.status.title == 'New' and bt.conjoined_primary is None])
872 1
873
874 >>> big_bug.canBeAQuestion()
875diff --git a/lib/lp/bugs/tests/test_bugsearch_conjoined.py b/lib/lp/bugs/tests/test_bugsearch_conjoined.py
876index 6e28a4a..1407278 100644
877--- a/lib/lp/bugs/tests/test_bugsearch_conjoined.py
878+++ b/lib/lp/bugs/tests/test_bugsearch_conjoined.py
879@@ -36,7 +36,7 @@ class TestSearchBase(TestCaseWithFactory):
880 return bug
881
882
883-class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
884+class TestProjectExcludeConjoinedPrimarySearch(TestSearchBase):
885 """Tests of exclude_conjoined_tasks param for project milestones."""
886
887 layer = DatabaseFunctionalLayer
888@@ -55,7 +55,7 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
889 user=None, milestone=self.milestone, exclude_conjoined_tasks=True)
890
891 def test_search_results_count_simple(self):
892- # Verify number of results with no conjoined masters.
893+ # Verify number of results with no conjoined primaries.
894 self.assertEqual(
895 self.bug_count,
896 self.bugtask_set.search(self.params).count())
897@@ -70,7 +70,7 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
898 self.assertThat(recorder, HasQueryCount(Equals(4)))
899
900 def test_search_results_count_with_other_productseries_tasks(self):
901- # Test with zero conjoined masters and bugtasks targeted to
902+ # Test with zero conjoined primaries and bugtasks targeted to
903 # productseries that are not the development focus.
904 productseries = self.factory.makeProductSeries(product=self.product)
905 extra_bugtasks = 0
906@@ -84,14 +84,14 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
907 self.bug_count + extra_bugtasks,
908 self.bugtask_set.search(self.params).count())
909
910- def test_search_results_count_with_conjoined_masters(self):
911- # Test with increasing numbers of conjoined masters.
912- # The conjoined masters will exclude the conjoined slaves from
913+ def test_search_results_count_with_conjoined_primarys(self):
914+ # Test with increasing numbers of conjoined primaries.
915+ # The conjoined primaries will exclude the conjoined replicas from
916 # the results.
917 tasks = list(self.bugtask_set.search(self.params))
918 for bug in self.bugs:
919 # The product bugtask is in the results before the conjoined
920- # master is added.
921+ # primary is added.
922 self.assertIn(
923 (bug.id, self.product),
924 [(task.bug.id, task.product) for task in tasks])
925@@ -104,17 +104,17 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
926 (bug.id, self.product),
927 [(task.bug.id, task.product) for task in tasks])
928
929- def test_search_results_count_with_wontfix_conjoined_masters(self):
930- # Test that conjoined master bugtasks in the WONTFIX status
931+ def test_search_results_count_with_wontfix_conjoined_primarys(self):
932+ # Test that conjoined primary bugtasks in the WONTFIX status
933 # don't cause the bug to be excluded.
934- masters = [
935+ primaries = [
936 self.factory.makeBugTask(
937 bug=bug, target=self.product.development_focus)
938 for bug in self.bugs]
939 tasks = list(self.bugtask_set.search(self.params))
940- wontfix_masters_count = 0
941- for bugtask in masters:
942- wontfix_masters_count += 1
943+ wontfix_primaries_count = 0
944+ for bugtask in primaries:
945+ wontfix_primaries_count += 1
946 self.assertNotIn(
947 (bugtask.bug.id, self.product),
948 [(task.bug.id, task.product) for task in tasks])
949@@ -122,14 +122,14 @@ class TestProjectExcludeConjoinedMasterSearch(TestSearchBase):
950 bugtask.transitionToStatus(
951 BugTaskStatus.WONTFIX, self.product.owner)
952 tasks = list(self.bugtask_set.search(self.params))
953- self.assertEqual(self.bug_count + wontfix_masters_count,
954+ self.assertEqual(self.bug_count + wontfix_primaries_count,
955 len(tasks))
956 self.assertIn(
957 (bugtask.bug.id, self.product),
958 [(task.bug.id, task.product) for task in tasks])
959
960
961-class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
962+class TestProjectGroupExcludeConjoinedPrimarySearch(TestSearchBase):
963 """Tests of exclude_conjoined_tasks param for project group milestones."""
964
965 layer = DatabaseFunctionalLayer
966@@ -151,7 +151,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
967 user=None, milestone=self.milestone, exclude_conjoined_tasks=True)
968
969 def test_search_results_count_simple(self):
970- # Verify number of results with no conjoined masters.
971+ # Verify number of results with no conjoined primaries.
972 self.assertEqual(
973 self.bug_count,
974 self.bugtask_set.search(self.params).count())
975@@ -166,7 +166,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
976 self.assertThat(recorder, HasQueryCount(Equals(4)))
977
978 def test_search_results_count_with_other_productseries_tasks(self):
979- # Test with zero conjoined masters and bugtasks targeted to
980+ # Test with zero conjoined primaries and bugtasks targeted to
981 # productseries that are not the development focus.
982 extra_bugtasks = 0
983 for bug, product in self.bug_products.items():
984@@ -180,8 +180,8 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
985 self.bug_count + extra_bugtasks,
986 self.bugtask_set.search(self.params).count())
987
988- def test_search_results_count_with_conjoined_masters(self):
989- # Test with increasing numbers of conjoined masters.
990+ def test_search_results_count_with_conjoined_primarys(self):
991+ # Test with increasing numbers of conjoined primaries.
992 tasks = list(self.bugtask_set.search(self.params))
993 for bug, product in self.bug_products.items():
994 self.assertIn(
995@@ -197,8 +197,8 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
996 (bug.id, product),
997 [(task.bug.id, task.product) for task in tasks])
998
999- def test_search_results_count_with_irrelevant_conjoined_masters(self):
1000- # Verify that a conjoined master in one project of the project
1001+ def test_search_results_count_with_irrelevant_conjoined_primarys(self):
1002+ # Verify that a conjoined primary in one project of the project
1003 # group doesn't cause a bugtask on another project in the group
1004 # to be excluded from the project group milestone's bugs.
1005 extra_bugtasks = 0
1006@@ -216,7 +216,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
1007 with person_logged_in(other_product.owner):
1008 other_product_bugtask.transitionToMilestone(
1009 other_product_milestone, other_product.owner)
1010- # Add conjoined master for the milestone on the new product.
1011+ # Add conjoined primary for the milestone on the new product.
1012 self.factory.makeBugTask(
1013 bug=bug, target=other_product.development_focus)
1014 # The bug count should not change, since we are just adding
1015@@ -225,15 +225,15 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
1016 self.bug_count + extra_bugtasks,
1017 self.bugtask_set.search(self.params).count())
1018
1019- def test_search_results_count_with_wontfix_conjoined_masters(self):
1020- # Test that conjoined master bugtasks in the WONTFIX status
1021+ def test_search_results_count_with_wontfix_conjoined_primarys(self):
1022+ # Test that conjoined primary bugtasks in the WONTFIX status
1023 # don't cause the bug to be excluded.
1024- masters = [
1025+ primaries = [
1026 self.factory.makeBugTask(
1027 bug=bug, target=product.development_focus)
1028 for bug, product in self.bug_products.items()]
1029 unexcluded_count = 0
1030- for bugtask in masters:
1031+ for bugtask in primaries:
1032 unexcluded_count += 1
1033 with person_logged_in(bugtask.target.owner):
1034 bugtask.transitionToStatus(
1035@@ -243,7 +243,7 @@ class TestProjectGroupExcludeConjoinedMasterSearch(TestSearchBase):
1036 self.bugtask_set.search(self.params).count())
1037
1038
1039-class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1040+class TestDistributionExcludeConjoinedPrimarySearch(TestSearchBase):
1041 """Tests of exclude_conjoined_tasks param for distribution milestones."""
1042
1043 layer = DatabaseFunctionalLayer
1044@@ -262,7 +262,7 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1045 user=None, milestone=self.milestone, exclude_conjoined_tasks=True)
1046
1047 def test_search_results_count_simple(self):
1048- # Verify number of results with no conjoined masters.
1049+ # Verify number of results with no conjoined primaries.
1050 self.assertEqual(
1051 self.bug_count,
1052 self.bugtask_set.search(self.params).count())
1053@@ -278,7 +278,7 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1054 self.assertThat(recorder, HasQueryCount(Equals(5)))
1055
1056 def test_search_results_count_with_other_productseries_tasks(self):
1057- # Test with zero conjoined masters and bugtasks targeted to
1058+ # Test with zero conjoined primaries and bugtasks targeted to
1059 # productseries that are not the development focus.
1060 distroseries = self.factory.makeDistroSeries(
1061 distribution=self.distro, status=SeriesStatus.SUPPORTED)
1062@@ -293,12 +293,12 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1063 self.bug_count + extra_bugtasks,
1064 self.bugtask_set.search(self.params).count())
1065
1066- def test_search_results_count_with_conjoined_masters(self):
1067- # Test with increasing numbers of conjoined masters.
1068+ def test_search_results_count_with_conjoined_primarys(self):
1069+ # Test with increasing numbers of conjoined primaries.
1070 tasks = list(self.bugtask_set.search(self.params))
1071 for bug in self.bugs:
1072 # The distro bugtask is in the results before the conjoined
1073- # master is added.
1074+ # primary is added.
1075 self.assertIn(
1076 (bug.id, self.distro),
1077 [(task.bug.id, task.distribution) for task in tasks])
1078@@ -311,19 +311,19 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1079 (bug.id, self.distro),
1080 [(task.bug.id, task.distribution) for task in tasks])
1081
1082- def test_search_results_count_with_wontfix_conjoined_masters(self):
1083- # Test that conjoined master bugtasks in the WONTFIX status
1084+ def test_search_results_count_with_wontfix_conjoined_primarys(self):
1085+ # Test that conjoined primary bugtasks in the WONTFIX status
1086 # don't cause the bug to be excluded.
1087- masters = [
1088+ primaries = [
1089 self.factory.makeBugTask(
1090 bug=bug, target=self.distro.currentseries)
1091 for bug in self.bugs]
1092- wontfix_masters_count = 0
1093+ wontfix_primaries_count = 0
1094 tasks = list(self.bugtask_set.search(self.params))
1095- for bugtask in masters:
1096- wontfix_masters_count += 1
1097+ for bugtask in primaries:
1098+ wontfix_primaries_count += 1
1099 # The distro bugtask is still excluded by the conjoined
1100- # master.
1101+ # primary.
1102 self.assertNotIn(
1103 (bugtask.bug.id, self.distro),
1104 [(task.bug.id, task.distribution) for task in tasks])
1105@@ -332,10 +332,10 @@ class TestDistributionExcludeConjoinedMasterSearch(TestSearchBase):
1106 BugTaskStatus.WONTFIX, self.distro.owner)
1107 tasks = list(self.bugtask_set.search(self.params))
1108 self.assertEqual(
1109- self.bug_count + wontfix_masters_count,
1110+ self.bug_count + wontfix_primaries_count,
1111 self.bugtask_set.search(self.params).count())
1112 # The distro bugtask is no longer excluded by the conjoined
1113- # master, since its status is WONTFIX.
1114+ # primary, since its status is WONTFIX.
1115 self.assertIn(
1116 (bugtask.bug.id, self.distro),
1117 [(task.bug.id, task.distribution) for task in tasks])
1118diff --git a/lib/lp/bugs/tests/test_bugwatch.py b/lib/lp/bugs/tests/test_bugwatch.py
1119index d595d08..402253b 100644
1120--- a/lib/lp/bugs/tests/test_bugwatch.py
1121+++ b/lib/lp/bugs/tests/test_bugwatch.py
1122@@ -360,7 +360,7 @@ class TestBugWatch(TestCaseWithFactory):
1123 [product_task], list(
1124 removeSecurityProxy(watch).bugtasks_to_update))
1125 # If we add a task such that the existing task becomes a
1126- # conjoined slave, only thr master task will be eligible for
1127+ # conjoined replica, only the primary task will be eligible for
1128 # update.
1129 product_series_task = self.factory.makeBugTask(
1130 bug=bug, target=product.development_focus)
1131diff --git a/lib/lp/registry/browser/__init__.py b/lib/lp/registry/browser/__init__.py
1132index cb372fb..c9b6f86 100644
1133--- a/lib/lp/registry/browser/__init__.py
1134+++ b/lib/lp/registry/browser/__init__.py
1135@@ -236,8 +236,8 @@ class RegistryDeleteViewMixin:
1136 # milestone, since it's still referenced.
1137 for bugtask in self._getBugtasks(milestone, ignore_privacy=True):
1138 nb = removeSecurityProxy(bugtask)
1139- if nb.conjoined_master is not None:
1140- Store.of(bugtask).remove(nb.conjoined_master)
1141+ if nb.conjoined_primary is not None:
1142+ Store.of(bugtask).remove(nb.conjoined_primary)
1143 else:
1144 nb.milestone = None
1145 removeSecurityProxy(milestone.all_specifications).set(milestoneID=None)
1146diff --git a/lib/lp/registry/browser/milestone.py b/lib/lp/registry/browser/milestone.py
1147index fb645c2..865b7f9 100644
1148--- a/lib/lp/registry/browser/milestone.py
1149+++ b/lib/lp/registry/browser/milestone.py
1150@@ -218,22 +218,22 @@ class MilestoneViewMixin:
1151 """The list of non-conjoined bugtasks targeted to this milestone."""
1152 # Put the results in a list so that iterating over it multiple
1153 # times in this method does not make multiple queries.
1154- non_conjoined_slaves = self.context.bugtasks(self.user)
1155+ non_conjoined_replicas = self.context.bugtasks(self.user)
1156 # Checking bug permissions is expensive. We know from the query that
1157 # the user has at least launchpad.View on the bugtasks and their bugs.
1158 # NB: this is in principle unneeded due to injection of permission in
1159 # the model layer now.
1160 precache_permission_for_objects(
1161- self.request, 'launchpad.View', non_conjoined_slaves)
1162+ self.request, 'launchpad.View', non_conjoined_replicas)
1163 precache_permission_for_objects(
1164 self.request, 'launchpad.View',
1165- [task.bug for task in non_conjoined_slaves])
1166+ [task.bug for task in non_conjoined_replicas])
1167 # We want the assignees loaded as we show them in the milestone home
1168 # page.
1169 list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
1170- [bug.assignee_id for bug in non_conjoined_slaves],
1171+ [bug.assignee_id for bug in non_conjoined_replicas],
1172 need_validity=True))
1173- return non_conjoined_slaves
1174+ return non_conjoined_replicas
1175
1176 @cachedproperty
1177 def _bug_badge_properties(self):
1178diff --git a/lib/lp/registry/model/milestone.py b/lib/lp/registry/model/milestone.py
1179index ed418c1..c6b5631 100644
1180--- a/lib/lp/registry/model/milestone.py
1181+++ b/lib/lp/registry/model/milestone.py
1182@@ -203,10 +203,10 @@ class MilestoneData:
1183 """The list of non-conjoined bugtasks targeted to this milestone."""
1184 # Put the results in a list so that iterating over it multiple
1185 # times in this method does not make multiple queries.
1186- non_conjoined_slaves = list(
1187+ non_conjoined_replicas = list(
1188 getUtility(IBugTaskSet).getPrecachedNonConjoinedBugTasks(
1189 user, self))
1190- return non_conjoined_slaves
1191+ return non_conjoined_replicas
1192
1193
1194 @implementer(IHasBugs, IMilestone, IBugSummaryDimension)
1195diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
1196index 09e1bca..b544ae3 100644
1197--- a/lib/lp/registry/model/person.py
1198+++ b/lib/lp/registry/model/person.py
1199@@ -1532,11 +1532,11 @@ class Person(
1200 bulk.load_related(Milestone, tasks, ['milestone_id'])
1201
1202 for task in tasks:
1203- # We skip masters (instead of slaves) from conjoined relationships
1204- # because we can do that without hittind the DB, which would not
1205- # be possible if we wanted to skip the slaves. The simple (but
1206- # expensive) way to skip the slaves would be to skip any tasks
1207- # that have a non-None .conjoined_master.
1208+ # We skip primaries (instead of replicas) from conjoined
1209+ # relationships because we can do that without hitting the DB,
1210+ # which would not be possible if we wanted to skip the replicas.
1211+ # The simple (but expensive) way to skip the replicas would be
1212+ # to skip any tasks that have a non-None .conjoined_primary.
1213 productseries = task.productseries
1214 distroseries = task.distroseries
1215 if productseries is not None and task.product is None:
1216@@ -1546,10 +1546,11 @@ class Person(
1217 continue
1218 elif distroseries is not None:
1219 candidate = None
1220- for possible_slave in tasks:
1221- sourcepackagename_id = possible_slave.sourcepackagename_id
1222+ for possible_replica in tasks:
1223+ sourcepackagename_id = (
1224+ possible_replica.sourcepackagename_id)
1225 if sourcepackagename_id == task.sourcepackagename_id:
1226- candidate = possible_slave
1227+ candidate = possible_replica
1228 # Distribution.currentseries is expensive to run for every
1229 # bugtask (as it goes through every series of that
1230 # distribution), but it's a cached property and there's only
1231@@ -2258,10 +2259,10 @@ class Person(
1232 coc.active = False
1233 params = BugTaskSearchParams(self, assignee=self)
1234 for bug_task in self.searchTasks(params):
1235- # If the bugtask has a conjoined master we don't try to
1236+ # If the bugtask has a conjoined primary we don't try to
1237 # update it, since we will update it correctly when we
1238- # update its conjoined master (see bug 193983).
1239- if bug_task.conjoined_master is not None:
1240+ # update its conjoined primary (see bug 193983).
1241+ if bug_task.conjoined_primary is not None:
1242 continue
1243
1244 # XXX flacoste 2007-11-26 bug=164635 The comparison using id in
1245diff --git a/lib/lp/registry/tests/test_milestonetag.py b/lib/lp/registry/tests/test_milestonetag.py
1246index 10ff914..d946961 100644
1247--- a/lib/lp/registry/tests/test_milestonetag.py
1248+++ b/lib/lp/registry/tests/test_milestonetag.py
1249@@ -161,7 +161,7 @@ class ProjectGroupMilestoneTagTest(TestCaseWithFactory):
1250 target=self.project_group, tags=tags)
1251 return items, milestonetag
1252
1253- # Add a test similar to TestProjectExcludeConjoinedMasterSearch in
1254+ # Add a test similar to TestProjectExcludeConjoinedPrimarySearch in
1255 # lp.bugs.tests.test_bugsearch_conjoined.
1256
1257 def test_bugtask_retrieve_single_milestone(self):
1258diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py
1259index 1e9ef75..10d8456 100644
1260--- a/lib/lp/registry/tests/test_person.py
1261+++ b/lib/lp/registry/tests/test_person.py
1262@@ -1770,7 +1770,7 @@ class Test_getAssignedBugTasksDueBefore(TestCaseWithFactory):
1263
1264 self.assertEqual(private_bug2.bugtasks, bugtasks)
1265
1266- def test_skips_distroseries_task_that_is_a_conjoined_master(self):
1267+ def test_skips_distroseries_task_that_is_a_conjoined_primary(self):
1268 distroseries = self.factory.makeDistroSeries()
1269 sourcepackagename = self.factory.makeSourcePackageName()
1270 sp = distroseries.getSourcePackage(sourcepackagename.name)
1271@@ -1780,38 +1780,38 @@ class Test_getAssignedBugTasksDueBefore(TestCaseWithFactory):
1272 milestone=milestone, target=sp.distribution_sourcepackage)
1273 removeSecurityProxy(bug).addTask(bug.owner, sp)
1274 self.assertEqual(2, len(bug.bugtasks))
1275- slave, master = bug.bugtasks
1276- self._assignBugTaskToTeamOwner(master)
1277- self.assertEqual(None, master.conjoined_master)
1278- self.assertEqual(master, slave.conjoined_master)
1279- self.assertEqual(slave.milestone, master.milestone)
1280- self.assertEqual(slave.assignee, master.assignee)
1281+ replica, primary = bug.bugtasks
1282+ self._assignBugTaskToTeamOwner(primary)
1283+ self.assertEqual(None, primary.conjoined_primary)
1284+ self.assertEqual(primary, replica.conjoined_primary)
1285+ self.assertEqual(replica.milestone, primary.milestone)
1286+ self.assertEqual(replica.assignee, primary.assignee)
1287
1288 bugtasks = list(self.team.getAssignedBugTasksDueBefore(
1289 self.today + timedelta(days=1), user=None))
1290
1291- self.assertEqual([slave], bugtasks)
1292+ self.assertEqual([replica], bugtasks)
1293
1294- def test_skips_productseries_task_that_is_a_conjoined_master(self):
1295+ def test_skips_productseries_task_that_is_a_conjoined_primary(self):
1296 milestone = self.factory.makeMilestone(dateexpected=self.today)
1297 removeSecurityProxy(milestone.product).development_focus = (
1298 milestone.productseries)
1299 bug = self.factory.makeBug(
1300 series=milestone.productseries, milestone=milestone)
1301 self.assertEqual(2, len(bug.bugtasks))
1302- slave, master = bug.bugtasks
1303+ replica, primary = bug.bugtasks
1304
1305 # This will cause the assignee to propagate to the other bugtask as
1306 # well since they're conjoined.
1307- self._assignBugTaskToTeamOwner(slave)
1308- self.assertEqual(master, slave.conjoined_master)
1309- self.assertEqual(slave.milestone, master.milestone)
1310- self.assertEqual(slave.assignee, master.assignee)
1311+ self._assignBugTaskToTeamOwner(replica)
1312+ self.assertEqual(primary, replica.conjoined_primary)
1313+ self.assertEqual(replica.milestone, primary.milestone)
1314+ self.assertEqual(replica.assignee, primary.assignee)
1315
1316 bugtasks = list(self.team.getAssignedBugTasksDueBefore(
1317 self.today + timedelta(days=1), user=None))
1318
1319- self.assertEqual([slave], bugtasks)
1320+ self.assertEqual([replica], bugtasks)
1321
1322 def _assignBugTaskToTeamOwnerAndSetMilestone(self, task, milestone):
1323 self._assignBugTaskToTeamOwner(task)

Subscribers

People subscribed via source and target branches

to status/vote changes: