Merge lp:~dpb/lp2kanban/lint-1 into lp:lp2kanban

Proposed by David Britton
Status: Merged
Approved by: David Britton
Approved revision: 178
Merged at revision: 146
Proposed branch: lp:~dpb/lp2kanban/lint-1
Merge into: lp:lp2kanban
Prerequisite: lp:~dpb/lp2kanban/feature-hoover-just-the-facts
Diff against target: 1106 lines (+194/-165)
8 files modified
Makefile (+4/-1)
src/lp2kanban/bugs2cards.py (+23/-20)
src/lp2kanban/cards2workitems.py (+6/-6)
src/lp2kanban/kanban.py (+38/-41)
src/lp2kanban/tests/test_bugs2cards.py (+94/-72)
src/lp2kanban/tests/test_cards2workitems.py (+13/-10)
src/lp2kanban/tests/test_description_annotations.py (+6/-5)
src/lp2kanban/workitems2cards.py (+10/-10)
To merge this branch: bzr merge lp:~dpb/lp2kanban/lint-1
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
🤖 Landscape Builder test results Approve
Review via email: mp+305280@code.launchpad.net

Commit message

Add lint target (flake8), remove lint.

Description of the change

Add lint target (flake8), remove lint.

make lint should pass
make ci-test includes lint check

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: http_proxy=$CI_PROXY https_proxy=$CI_PROXY make ci-test
Result: Success
Revno: 178
Branch: lp:~davidpbritton/lp2kanban/lint-1
Jenkins: https://ci.lscape.net/job/latch-test/10190/

review: Approve (test results)
Revision history for this message
Alberto Donato (ack) wrote :

nice! +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-08-29 20:19:31 +0000
3+++ Makefile 2016-09-08 22:02:56 +0000
4@@ -61,4 +61,7 @@
5 . venv/bin/activate; \
6 $(MAKE) check
7
8-.PHONY: check default configs needs-xdg-utils clean ci-test
9+lint:
10+ flake8 src/lp2kanban
11+
12+.PHONY: check default configs needs-xdg-utils clean ci-test lint
13
14=== modified file 'src/lp2kanban/bugs2cards.py'
15--- src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000
16+++ src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000
17@@ -141,7 +141,7 @@
18 self.lp_to_group.update(parse_groups_config(groups_config))
19
20 self.feature_lanes = {}
21- new_lane = config.get("new_lanes")
22+ new_lane = config.get("new_lanes")
23 if not new_lane:
24 return
25 if "${group}" in new_lane:
26@@ -149,7 +149,7 @@
27 for group in set(self.lp_to_group.values()):
28 lane_path = new_lane.replace("${group}", group)
29 lane = board.getLaneByPath(lane_path)
30- group_tag = "squad-%s" % group.lower().replace(" ","-")
31+ group_tag = "squad-%s" % group.lower().replace(" ", "-")
32 self.feature_lanes[group_tag] = lane
33 for card in board.getFeatureCards():
34 if not card.tags:
35@@ -192,6 +192,7 @@
36 IGNORE_STATUSES = ('Superseded',)
37 FINAL_STATUSES = ('Approved', 'Merged')
38
39+
40 def get_lp_bug(card, launchpad):
41 """Return the launchpad bug resource for a card if that bug exists.
42
43@@ -273,7 +274,7 @@
44
45 for card in feature_card.lane.board.cards:
46 if card_type and card.type.name != card_type:
47- continue
48+ continue
49 if card.tags:
50 card_tags = set(card.tags.split(","))
51 if feature_tags.intersection(card_tags):
52@@ -304,7 +305,7 @@
53 mps = []
54 has_inprogress_branches = False
55 for branch in branches:
56- if hasattr(branch, 'branch') :
57+ if hasattr(branch, 'branch'):
58 branch = branch.branch
59 for mp in branch.landing_targets:
60 mp_info = Record(rank=None, status=None, mp=None)
61@@ -319,7 +320,7 @@
62 mps.append(mp_info)
63 if len(mps) == 0:
64 return None
65- mps.sort(key=lambda x:(x.rank, x.mp.date_created), reverse=True)
66+ mps.sort(key=lambda x: (x.rank, x.mp.date_created), reverse=True)
67 if not has_inprogress_branches:
68 return mps[0] # Return highest-ranked and most-recent MP
69 for mp in mps:
70@@ -395,7 +396,7 @@
71 return False
72 if card.tags and "no-sync" in set(card.tags.split(",")):
73 return False
74-
75+
76 enabled = conf.get('move_cards', 'off') == 'on'
77 active = not card.lane.board.is_archived
78 return enabled and active
79@@ -498,7 +499,7 @@
80 status = CardStatus.LANDING
81 elif branch_card_status:
82 return branch_card_status
83-
84+
85 elif bug_status in DONE_FIX_BUG_STATUSES:
86 status = CardStatus.DONE_FIX
87 elif bug_status in DONE_NOFIX_BUG_STATUSES:
88@@ -512,7 +513,7 @@
89 if task.target in all_projects:
90 return True
91 if (hasattr(task.target, "project") and
92- task.target.project in all_projects):
93+ task.target.project in all_projects):
94 return True
95 return False
96
97@@ -525,13 +526,13 @@
98 for task in tasks:
99 # Ignore bug task status for series targets
100 if (task.target.resource_type_link == RESOURCE_TYPE_LINK_SERIES and
101- task.target.project in all_projects):
102+ task.target.project in all_projects):
103 continue # Skip series task status for our projects
104 if (task.assignee is not None and
105- task.assignee.name not in all_users):
106+ task.assignee.name not in all_users):
107 continue # Skip non-users tasks
108 if (task.target in all_projects or
109- task.assignee is not None):
110+ task.assignee is not None):
111 matches = True
112 if (lowest_status is None or
113 (ORDERED_BUG_STATUSES.index(task.status) <
114@@ -555,7 +556,6 @@
115 return possible_target_lane
116
117
118-
119 def find_card_target_lane_next(lane, path):
120 """Find the target lane for the path using the "next" algorithm.
121
122@@ -723,7 +723,7 @@
123 assignee = Record(name=owner_name)
124 try:
125 move_card(card, card_status, bconf, [assignee], lp_users)
126- except IOError as e:
127+ except IOError:
128 log.exception("Error moving card: %s (in %s)" % (
129 card.title, card_path))
130 continue
131@@ -751,7 +751,8 @@
132 continue
133 ignore_external_projects = (
134 bconf.get("autosync") == "active-projects")
135- if ignore_external_projects and not is_bug_in_projects(lp_bug, lp_projects):
136+ if ignore_external_projects and not is_bug_in_projects(
137+ lp_bug, lp_projects):
138 continue # Skip bugs in external projects
139 synced, card_status, assignees, mp_url, mp_type = get_bug_status(
140 lp_bug, lp_projects, lp_users.lp_to_kanban.keys())
141@@ -766,7 +767,7 @@
142 if card_status is not None and should_move_card(card, bconf):
143 try:
144 move_card(card, card_status, bconf, assignees, lp_users)
145- except IOError as e:
146+ except IOError:
147 log.exception("Error Moving: lp:%s: %s (in %s)" % (
148 lp_bug.id, card.title, card_path))
149 continue
150@@ -878,7 +879,8 @@
151 return
152
153 log.info(
154- u" * Moving card#{}: {} -> {}".format(card.id, card.lane.path, target_lane_path))
155+ u" * Moving card#{}: {} -> {}".format(
156+ card.id, card.lane.path, target_lane_path))
157 card.move(target_lane)
158
159
160@@ -896,8 +898,8 @@
161 # happens.
162 try:
163 card.save()
164- except IOError as e:
165- log.exception(u"Exception caught. Skipping board.\n"
166+ except IOError:
167+ log.exception("Exception caught. Skipping board.\n"
168 " desc:{}\n"
169 " kanban id:{}\n"
170 " LP bug id:{}\n".format(
171@@ -937,7 +939,8 @@
172
173 if args.debug:
174 logging.basicConfig(
175- level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s')
176+ level=logging.DEBUG,
177+ format='%(asctime)s %(levelname)s %(message)s')
178 import httplib
179 httplib.HTTPConnection.debuglevel = 1
180 else:
181@@ -975,7 +978,7 @@
182 continue
183 bconf = boards_config[board_name]
184 process_board(board, bconf, args.bug, args.card)
185- except (KeyError, AssertionError) as e:
186+ except (KeyError, AssertionError):
187 log.exception("Exception caught. Skipping board.")
188 continue
189
190
191=== modified file 'src/lp2kanban/cards2workitems.py'
192--- src/lp2kanban/cards2workitems.py 2013-06-19 19:44:20 +0000
193+++ src/lp2kanban/cards2workitems.py 2016-09-08 22:02:56 +0000
194@@ -13,9 +13,9 @@
195 # support POSTPONED work items in this mapping, since the Kanban board
196 # has no such concept.
197 LANE_TYPE_TO_WORK_ITEM_STATUS_MAP = {
198- 1: u"TODO", # Kanban type "Ready"
199- 2: u"INPROGRESS", # Kanban type "In process"
200- 3: u"DONE", # Kanban type "Complete"
201+ 1: u"TODO", # Kanban type "Ready"
202+ 2: u"INPROGRESS", # Kanban type "In process"
203+ 3: u"DONE", # Kanban type "Complete"
204 }
205
206
207@@ -34,7 +34,7 @@
208 card.description_annotations.get('blueprint_url') or
209 card.external_system_url)
210 if (blueprint_url is None or not
211- blueprint_url_pattern.match(blueprint_url)):
212+ blueprint_url_pattern.match(blueprint_url)):
213 # Return early if the card isn't linked to a blueprint, since
214 # there's no point doing the conversion if we're not going to
215 # sync it.
216@@ -118,8 +118,8 @@
217 work_items_text = "\n".join(work_items)
218 blueprint = fetch_blueprint(lp, blueprint_url)
219 if blueprint is not None:
220- print "Adding %s work items to %s" % (
221- len(work_items), blueprint.name)
222+ print("Adding %s work items to %s" % (
223+ len(work_items), blueprint.name))
224 blueprint.workitems_text = work_items_text
225 blueprint.lp_save()
226
227
228=== modified file 'src/lp2kanban/kanban.py'
229--- src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000
230+++ src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000
231@@ -3,7 +3,6 @@
232
233 import requests
234 import json
235-import logging
236 import operator
237 from pprint import pprint
238 import re
239@@ -112,14 +111,14 @@
240
241 resp = request.response
242 if (not sent or
243- resp.status_code not in LeankitResponseCodes.SUCCESS_CODES):
244- print "Error from kanban"
245+ resp.status_code not in LeankitResponseCodes.SUCCESS_CODES):
246+ print("Error from kanban")
247 pprint(resp)
248 raise IOError('kanban error %d' % (resp.status_code))
249 response = Record(json.loads(resp.content))
250
251 if (handle_errors and
252- response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES):
253+ response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES):
254 raise IOError('kanban error %d: %s' % (
255 response.ReplyCode, response.ReplyText))
256 return response
257@@ -166,7 +165,7 @@
258 def _prettifyName(self, camelcase):
259 camelcase = camelcase.replace('ID', '_id')
260 if len(camelcase) > 1:
261- repl_func = lambda match: '_' + match.group(1).lower()
262+ def repl_func(match): return '_' + match.group(1).lower()
263 camelcase = camelcase[0].lower() + camelcase[1:]
264 return re.sub('([A-Z])', repl_func, camelcase)
265 else:
266@@ -174,7 +173,7 @@
267
268 def _toCamelCase(self, name):
269 if len(name) > 1:
270- repl_func = lambda match: match.group(1)[1:].upper()
271+ def repl_func(match): return match.group(1)[1:].upper()
272 name = name[0].upper() + name[1:]
273 return re.sub('(_[a-z])', repl_func, name)
274 else:
275@@ -236,9 +235,9 @@
276 # no-op.
277 return
278 data = self._raw_data
279- data["UserWipOverrideComment"] = None;
280+ data["UserWipOverrideComment"] = None
281 if ("AssignedUsers" in data and
282- "assigned_user_id" not in self.dirty_attrs):
283+ "assigned_user_id" not in self.dirty_attrs):
284 if 'AssignedUserId' in data.keys():
285 del data['AssignedUserId']
286 if 'AssignedUserName' in data.keys():
287@@ -247,7 +246,6 @@
288 lambda X: X['AssignedUserId'], data['AssignedUsers'])
289
290 for attr in self.dirty_attrs:
291- #print "Storing %s in %s..." % (attr, self._toCamelCase(attr))
292 data[self._toCamelCase(attr)] = getattr(self, attr)
293
294 board_id = str(self.lane.board.id)
295@@ -380,7 +378,6 @@
296 self.external_card_id = src.external_card_id
297 self.assigned_user_id = src.assigned_user_id
298
299-
300 @property
301 def parsed_description(self):
302 """Parse the card description to find key=value pairs.
303@@ -397,8 +394,8 @@
304 end = match.end()
305 try:
306 annotations = Record(json.loads(self.description[start:end]))
307- except ValueError, ex:
308- print "Unable to parse card %s: %s" % (self.id, ex.message)
309+ except ValueError as ex:
310+ print("Unable to parse card %s: %s" % (self.id, ex.message))
311 annotations = Record()
312 return (
313 annotations,
314@@ -580,7 +577,7 @@
315 archive_lanes = [lane_dict['Lane'] for lane_dict in self._archive]
316 archive_lanes.extend(
317 [lane_dict['Lane'] for
318- lane_dict in self._archive[0]['ChildLanes']])
319+ lane_dict in self._archive[0]['ChildLanes']])
320 lanes += archive_lanes
321 lanes += self.connector.get(
322 "/Kanban/Api/Board/" + str(self.id) + "/Backlog").ReplyData[0]
323@@ -594,7 +591,7 @@
324
325 def _classifyCards(self):
326 """Classify the cards into buckets for lookups later."""
327- print " Classifying cards %s cards." % len(self.cards)
328+ print(" Classifying cards %s cards." % len(self.cards))
329 for card in self.cards:
330 if card.type.name == "Feature":
331 self._feature_cards.add(card)
332@@ -608,18 +605,18 @@
333 if BRANCH_MP_REGEX.match(card.external_system_url):
334 self._cards_with_branches.add(card)
335 else:
336- print ("WARNING: Card won't sync. Non-branch link ({}) "
337- "for {}.".format(url, card.title))
338- print " - %s feature cards" % len(
339- self._feature_cards)
340- print " - %s cards with external ids" % len(
341- self._cards_with_external_ids)
342- print " - %s cards with external links" % len(
343- self._cards_with_external_links)
344- print " - %s cards with description annotations" % len(
345- self._cards_with_description_annotations)
346- print " - %s cards with branches" % len(
347- self._cards_with_branches)
348+ print("WARNING: Card won't sync. Non-branch link ({}) "
349+ "for {}.".format(url, card.title))
350+ print(" - %s feature cards" % len(
351+ self._feature_cards))
352+ print(" - %s cards with external ids" % len(
353+ self._cards_with_external_ids))
354+ print(" - %s cards with external links" % len(
355+ self._cards_with_external_links))
356+ print(" - %s cards with description annotations" % len(
357+ self._cards_with_description_annotations))
358+ print(" - %s cards with branches" % len(
359+ self._cards_with_branches))
360
361 def _populateFeatureTagPaths(self):
362 """Create a map from feature tag to the group owning the feature."""
363@@ -672,12 +669,13 @@
364
365 def getLane(self, lane_id):
366 flat_lanes = {}
367+
368 def flatten_lane(lane):
369 flat_lanes[lane.id] = lane
370 for child in lane.child_lanes:
371 flatten_lane(child)
372 map(flatten_lane, self.root_lane.child_lanes)
373- return flat_lanes[lane_id];
374+ return flat_lanes[lane_id]
375
376 def getLaneByTitle(self, title):
377 if len(self.root_lane.child_lanes) > 0:
378@@ -689,7 +687,7 @@
379 else:
380 for child in lane.child_lanes:
381 result = self._getLaneByTitle(child, title)
382- if result != None:
383+ if result is not None:
384 return result
385 return None
386
387@@ -698,7 +696,7 @@
388 return self._getLaneByPath(self.root_lane, path, ignorecase)
389
390 def _getLaneByPath(self, lane, path, ignorecase):
391- if ignorecase == True:
392+ if ignorecase is True:
393 if lane.path.lower() == path.lower():
394 return lane
395 else:
396@@ -707,24 +705,23 @@
397
398 for child in lane.child_lanes:
399 result = self._getLaneByPath(child, path, ignorecase)
400- if result != None:
401+ if result is not None:
402 return result
403 return None
404
405-
406 def _printLanes(self, lane, indent, include_cards=False):
407 next_lane = lane.getNextLanes()
408 if next_lane is None:
409 next_lane = ''
410 else:
411 next_lane = (' (next: any of [' +
412- ', '.join([my_lane.path for my_lane in next_lane])
413- + '])')
414+ ', '.join([my_lane.path for my_lane in next_lane]) +
415+ '])')
416 next_lane += ' - %d cards' % len(lane.cards)
417- print " " * indent + "* " + lane.title + next_lane
418+ print(" " * indent + "* " + lane.title + next_lane)
419 for card in lane.cards:
420- print (" " * (indent + 1) + "- #" + card.external_card_id +
421- ': ' + card.title)
422+ print(" " * (indent + 1) + "- #" + card.external_card_id +
423+ ': ' + card.title)
424 for child in lane.child_lanes:
425 self._printLanes(child, indent + 1)
426
427@@ -732,7 +729,7 @@
428 """Recursively prints all the lanes in the board with indentation."""
429 if len(self.root_lane.child_lanes) == 0:
430 return
431- print "Board lanes:"
432+ print("Board lanes:")
433 indent = 1
434 for lane in self.root_lane.child_lanes:
435 self._printLanes(lane, indent, include_cards)
436@@ -818,16 +815,16 @@
437 kanban = LeankitKanban('launchpad.leankitkanban.com',
438 'user@email', 'password')
439
440- print "Active boards:"
441+ print("Active boards:")
442 boards = kanban.getBoards()
443 for board in boards:
444- print " * %s (%d)" % (board.title, board.id)
445+ print(" * %s (%d)" % (board.title, board.id))
446
447 # Get a board by the title.
448 board_name = 'Landscape 2016'
449- print "Getting board '%s'..." % board_name
450+ print("Getting board '%s'..." % board_name)
451 board = kanban.getBoard(title=board_name)
452 board.printLanes()
453
454 # Print all users.
455- print board.users
456+ print(board.users)
457
458=== modified file 'src/lp2kanban/tests/test_bugs2cards.py'
459--- src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000
460+++ src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000
461@@ -1,8 +1,6 @@
462 # Copyright 2011-2012 Canonical Ltd
463 """Tests for the bugs2cards.py script."""
464
465-__metaclass__ = type
466-
467 from ConfigParser import (
468 ConfigParser,
469 NoSectionError,
470@@ -38,12 +36,9 @@
471 )
472 from lp2kanban.kanban import (
473 RESOURCE_TYPE_LINK_SERIES,
474- LeankitCard,
475 Record,
476 )
477 from lp2kanban.tests.common import (
478- FauxLeankitUser,
479- FauxLaunchpadUser,
480 FauxLaunchpadUsersForBoard,
481 FauxBoard,
482 FauxCard,
483@@ -51,6 +46,8 @@
484 FauxLane,
485 )
486
487+__metaclass__ = type
488+
489
490 def parse_config_text(text):
491 config = ConfigParser()
492@@ -90,8 +87,7 @@
493 globals, boards = parse_config(config)
494
495 self.assertEqual({'var1': 'value1',
496- 'var2': 'value2',
497- }, globals)
498+ 'var2': 'value2'}, globals)
499 self.assertEqual({'Board 1': {'var1': 'board value 1'}}, boards)
500
501 def test_parse_config_defaults(self):
502@@ -131,7 +127,9 @@
503
504
505 class FauxMP:
506- def __init__(self, queue_status, target_name, link=None, description=None, date_created=None):
507+ def __init__(
508+ self, queue_status, target_name, link=None, description=None,
509+ date_created=None):
510 self.queue_status = queue_status
511 self.target_branch = Record(name=target_name)
512 self.web_link = link
513@@ -145,7 +143,7 @@
514 self.projects = data.get("projects", {})
515 self.project_group = data.get("project_groups", {})
516 self.bugs = data.get("bugs", {})
517-
518+
519
520 class BranchInfoTests(unittest.TestCase):
521
522@@ -296,7 +294,7 @@
523 def __init__(self, resource_type_link="http://api/project", project=None):
524 self.resource_type_link = resource_type_link
525 self.project = project
526-
527+
528
529 class FauxBugTask:
530
531@@ -312,23 +310,25 @@
532 class IsBugInProjectsTest(unittest.TestCase):
533
534 def test_false_when_part_of_external_project(self):
535- # Return False when the bug is part of an unconfigured (external) project
536+ # Return False when the bug is part of an unconfigured (external)
537+ # project
538 bug = FauxBug()
539- project = FauxTarget(resource_type_link="project", project="otherproject")
540+ project = FauxTarget(
541+ resource_type_link="project", project="otherproject")
542 bug.addTask(project)
543 self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
544
545 def test_true_when_part_of_configured_projects(self):
546 # Return True when the bug is one of the configured (internal) projects
547 bug = FauxBug()
548- project = FauxTarget(resource_type_link="project", project="myproject2")
549+ project = FauxTarget(
550+ resource_type_link="project", project="myproject2")
551 bug.addTask(project)
552 self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
553
554-
555 def test_true_when_series_within_a_configured_project(self):
556- # Return True when the bug has a series task that is related to one of the
557- # configured projects.
558+ # Return True when the bug has a series task that is related to one
559+ # of the configured projects.
560 bug = FauxBug()
561 series = FauxTarget(
562 resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1")
563@@ -407,7 +407,8 @@
564 # than 'Fix Committed').
565 bug = FauxBug()
566 project1 = FauxTarget(resource_type_link="project", project="lazr")
567- project2 = FauxTarget(resource_type_link="project", project="launchpad")
568+ project2 = FauxTarget(
569+ resource_type_link="project", project="launchpad")
570 bug.addTask(project1, status='Fix Released')
571 bug.addTask(project2, status='In Progress')
572 matches, status, assignees, mp_url, mp_type = get_bug_status(
573@@ -583,8 +584,9 @@
574 self.assertTrue(should_sync_card(card, {'autosync': 'on',
575 'sync_cards': 'on'}))
576
577- def test_should_sync_card_autosync_synced_with_external_card_id(self):
578- # When autosync is 'active-projects', cards with external card IDs are synced.
579+ def test_should_sync_card_autosync_active_projects_external_card_id(self):
580+ # When autosync is 'active-projects', cards with external card
581+ # IDs are synced.
582 card = Record(title=u'no sync', description=u'',
583 external_card_id=u'11', external_system_url=u'')
584 self.assertTrue(should_sync_card(card, {'autosync': 'active-projects',
585@@ -663,8 +665,8 @@
586 self.assertFalse(should_sync_card(card2, conf))
587
588 def test_get_card_status_new_no_branch(self):
589- # For a bug in 'New', 'Triaged' or 'Confirmed' or 'Incomplete' statuses,
590- # the branch status is new state.
591+ # For a bug in 'New', 'Triaged' or 'Confirmed' or
592+ # 'Incomplete' statuses the branch status is new state.
593 no_branch_info = Record(status=None)
594 self.assertEqual(
595 CardStatus.NEW,
596@@ -681,29 +683,29 @@
597
598 def test_get_card_status_coding_no_bug(self):
599 # For a card with no associated bug, an 'In Progress' branch status
600- # will be in the coding state.
601+ # will be in the coding state.
602 branch_info = Record(status='In Progress', target=None)
603 self.assertEqual(
604- CardStatus.CODING,
605+ CardStatus.CODING,
606 get_card_status(None, [], branch_info))
607
608 def test_get_card_status_review_no_bug(self):
609 # For a card with no associated bug, an 'Approved' or 'In Review'
610- # branch status will be in the review state.
611+ # branch status will be in the review state.
612 branch_infos = [
613 Record(status='In Review', target=None),
614 Record(status='Approved', target=None)]
615 for branch_info in branch_infos:
616 self.assertEqual(
617- CardStatus.REVIEW,
618+ CardStatus.REVIEW,
619 get_card_status(None, [], branch_info))
620
621 def test_get_card_status_landing_no_bug(self):
622 # For a card with no associated bug, a merged branch status will be in
623- # the landing state.
624+ # the landing state.
625 branch_info = Record(status='Merged', target=None)
626 self.assertEqual(
627- CardStatus.LANDING,
628+ CardStatus.LANDING,
629 get_card_status(None, [], branch_info))
630
631 def test_get_card_status_coding_in_progress_no_branch(self):
632@@ -888,7 +890,7 @@
633 cards = get_cards_for_feature(
634 self.feature_card, self.feature_bug, card_type='Branch')
635 self.assertItemsEqual([branch_card], cards)
636-
637+
638
639 class FindLaneNextTest(unittest.TestCase):
640
641@@ -980,7 +982,7 @@
642 def test_no_bugs(self):
643 # If no bugs are found, no cards are created.
644 board = FauxBoard()
645- bug_lane = board.addLane('bugs')
646+ board.addLane('bugs')
647 project = FauxLaunchpadProject(bug_tasks=[])
648 create_cards_in_project(board, project, 'kanban')
649 self.assertEqual([], board.cards)
650@@ -989,7 +991,7 @@
651 # If a new bug is found, a card is created for it, having the
652 # bug id, title and description saved.
653 board = FauxBoard()
654- bug_lane = board.addLane('bugs')
655+ board.addLane('bugs')
656 project = FauxLaunchpadProject(
657 bug_tasks=[FauxBugTask(
658 bug_id=42, title='Test bug',
659@@ -1007,7 +1009,7 @@
660 # After a card is created from a bug, the bug tag is removed, so
661 # that having a lot of open bugs won't slow down the process.
662 board = FauxBoard()
663- bug_lane = board.addLane('bugs')
664+ board.addLane('bugs')
665 project = FauxLaunchpadProject(
666 bug_tasks=[FauxBugTask(
667 bug_id=42, title='Test bug',
668@@ -1038,7 +1040,7 @@
669 # Cards are created only for bugs having the bug tag that is
670 # passed in to create_cards_in_project().
671 board = FauxBoard()
672- bug_lane = board.addLane('bugs')
673+ board.addLane('bugs')
674 project = FauxLaunchpadProject(
675 bug_tasks=[FauxBugTask(
676 bug_id=21, title='Tagged with other tag',
677@@ -1058,7 +1060,7 @@
678 # It's possible to specify which card type that the created card
679 # should have, instead of the default one.
680 board = FauxBoard()
681- bug_lane = board.addLane('bugs')
682+ board.addLane('bugs')
683 project = FauxLaunchpadProject(
684 bug_tasks=[FauxBugTask(
685 bug_id=42, title='Test bug',
686@@ -1066,7 +1068,8 @@
687 tags=['kanban'])])
688 feature_type = FauxCardType(id=2, name='Feature', is_default=False)
689 board.cardtypes[feature_type.id] = feature_type
690- create_cards_in_project(board, project, 'kanban', cardtype_name='Feature')
691+ create_cards_in_project(
692+ board, project, 'kanban', cardtype_name='Feature')
693 [card] = board.cards
694 self.assertEqual(feature_type.id, card.type_id)
695 self.assertTrue(card.saved)
696@@ -1075,8 +1078,8 @@
697 # It's possible to specify to which lane the card should be
698 # added.
699 board = FauxBoard()
700- bug_lane = board.addLane('bugs')
701- bug_lane = board.addLane('incoming')
702+ board.addLane('bugs')
703+ board.addLane('incoming')
704 project = FauxLaunchpadProject(
705 bug_tasks=[FauxBugTask(
706 bug_id=42, title='Test bug',
707@@ -1094,7 +1097,7 @@
708 # Bug descriptions that exceed the 19997 character limit of a
709 # kanban card are truncated to 19500 bytes.
710 board = FauxBoard()
711- bug_lane = board.addLane('bugs')
712+ board.addLane('bugs')
713 project = FauxLaunchpadProject(
714 bug_tasks=[FauxBugTask(
715 bug_id=42, title='Test bug',
716@@ -1185,7 +1188,6 @@
717 parse_groups_config(config))
718
719
720-
721 class LaunchpadUsersForBoardTest(unittest.TestCase):
722
723 def setUp(self):
724@@ -1266,12 +1268,12 @@
725 lp_users = LaunchpadUsersForBoard()
726 lp_users.set_up_users(lp, board, config)
727 self.assertEqual(
728- {"sometag": coding_lane, "anothertag": coding_lane},
729+ {"sometag": coding_lane, "anothertag": coding_lane},
730 lp_users.feature_lanes)
731
732 def test_feature_lanes_unset_with_group_new_lanes_and_no_assignee(self):
733 # When a feature card is present and tagged and the new_lanes
734- # in the config specifies the ${group} variable,
735+ # in the config specifies the ${group} variable,
736 # feature_lanes will be unset when there are no assigned users on the
737 # card.
738 feature_card = FauxCard(tags="sometag")
739@@ -1305,7 +1307,8 @@
740 lp = None
741 config = {"groups_config_file": groups_file.name,
742 "new_lanes": "dev::${group}::coding"}
743- lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")})
744+ lp_users = LaunchpadUsersForBoard(
745+ kanban_to_lp={1: FauxPerson("User1")})
746 lp_users.set_up_users(lp, board, config)
747 self.assertEqual(
748 {"squad-group-1": coding_lane, "squad-group-2": coding_lane2},
749@@ -1313,7 +1316,7 @@
750
751 def test_feature_lanes_set_with_group_new_lanes_and_assignee(self):
752 # When a feature card is present and tagged and the new_lanes
753- # in the config specifies the ${group} variable,
754+ # in the config specifies the ${group} variable,
755 # feature_lanes will be set when there are is an assigned user on the
756 # card.
757 feature_card = FauxCard(tags="sometag,anothertag")
758@@ -1336,17 +1339,17 @@
759 lp = None
760 config = {"groups_config_file": groups_file.name,
761 "new_lanes": "dev::${group}::coding"}
762- lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")})
763+ lp_users = LaunchpadUsersForBoard(
764+ kanban_to_lp={1: FauxPerson("User1")})
765 lp_users.set_up_users(lp, board, config)
766 self.assertEqual(
767- {"sometag": coding_lane, "anothertag": coding_lane,
768- "squad-group-1": coding_lane, "squad-group-2": coding_lane2},
769+ {"sometag": coding_lane, "anothertag": coding_lane,
770+ "squad-group-1": coding_lane, "squad-group-2": coding_lane2},
771 lp_users.feature_lanes)
772
773
774 class FauxPerson:
775
776-
777 def __init__(self, name):
778 self.name = name
779
780@@ -1654,7 +1657,7 @@
781 self.lp_users = FauxLaunchpadUsersForBoard()
782
783 def test_sync_cards_moves_landing_cards_to_taskboard_in_landing_lane(self):
784- """
785+ """
786 Feature-linked cards in landing lanes will move to taskboard when the
787 feature card is in landing_lanes.
788 """
789@@ -1735,16 +1738,20 @@
790 self.assertEqual((self.feature_card, 'Done'), landed_card2.moved_to)
791
792 def test_sync_cards_external_ids_filter_card(self):
793- """filter_card skips and selects correctly."""
794- # Configure bugs2cards to sync only active-projects myproject1 and myproject2
795+ """filter_card skips and selects correctly."""
796+ # Configure bugs2cards to sync only active-projects myproject1
797+ # and myproject2
798 self.bconf.update({
799 "autosync": "active-projects",
800 "projects": "myproject",
801 "sync_cards": "on", "move_cards": "on",
802 "new_lanes": "new"})
803- # Setup bug1 and bug2 in separate projects which both need to move coding -> new
804- project1 = FauxTarget(resource_type_link="project", project="myproject")
805- lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})
806+ # Setup bug1 and bug2 in separate projects which both need to
807+ # move coding -> new
808+ project1 = FauxTarget(
809+ resource_type_link="project", project="myproject")
810+ lp_users = FauxLaunchpadUsersForBoard(
811+ lp_to_kanban={"person1": "Person One"})
812 lp_projects = [project1]
813 bug_task1 = FauxBugTask(
814 bug_id=123, target=project1, title='Test bug in myproject',
815@@ -1766,25 +1773,31 @@
816 bug_card1.type = bug_type
817 bug_card2.type = bug_type
818 self.board.cards.extend([bug_card1, bug_card2])
819- lp = FauxLP({"projects": {"myproject": project1},
820- "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
821+ lp = FauxLP({
822+ "projects": {"myproject": project1},
823+ "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
824 sync_cards_external_ids(
825- board, self.bconf, lp, lp_users, lp_projects, filter_card=bug_card2.id)
826+ board, self.bconf, lp, lp_users,
827+ lp_projects, filter_card=bug_card2.id)
828 coding_lane = FauxLane(board=board, path='coding')
829 self.assertEqual(new_lane, bug_card2.moved_to)
830 self.assertIsNone(bug_card1.moved_to)
831
832 def test_sync_cards_skips_bugs_not_in_filter_bug(self):
833- """filter_bug skips and selects bugs in sync_cards_external_ids."""
834- # Configure bugs2cards to sync only active-projects myproject1 and myproject2
835+ """filter_bug skips and selects bugs in sync_cards_external_ids."""
836+ # Configure bugs2cards to sync only active-projects myproject1
837+ # and myproject2
838 self.bconf.update({
839 "autosync": "active-projects",
840 "projects": "myproject",
841 "sync_cards": "on", "move_cards": "on",
842 "new_lanes": "new"})
843- # Setup bug1 and bug2 in separate projects which both need to move coding -> new
844- project1 = FauxTarget(resource_type_link="project", project="myproject")
845- lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})
846+ # Setup bug1 and bug2 in separate projects which both need
847+ # to move coding -> new
848+ project1 = FauxTarget(
849+ resource_type_link="project", project="myproject")
850+ lp_users = FauxLaunchpadUsersForBoard(
851+ lp_to_kanban={"person1": "Person One"})
852 lp_projects = [project1]
853 bug_task1 = FauxBugTask(
854 bug_id=123, target=project1, title='Test bug in myproject',
855@@ -1806,26 +1819,34 @@
856 bug_card1.type = bug_type
857 bug_card2.type = bug_type
858 self.board.cards.extend([bug_card1, bug_card2])
859- lp = FauxLP({"projects": {"myproject": project1},
860- "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
861+ lp = FauxLP({
862+ "projects": {"myproject": project1},
863+ "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
864 sync_cards_external_ids(
865 board, self.bconf, lp, lp_users, lp_projects, filter_bug=u"456")
866 coding_lane = FauxLane(board=board, path='coding')
867 self.assertEqual(new_lane, bug_card2.moved_to)
868 self.assertIsNone(bug_card1.moved_to)
869
870- def test_sync_cards_skips_bugs_of_external_projects_when_active_projects_set(self):
871- """Bugs of external projects are skipped when autosync set to active-projects."""
872- # Configure bugs2cards to sync only active-projects myproject1 and myproject2
873+ def test_sync_cards_skips_bugs_of_external_projects_active_projects(self):
874+ """Bugs of external projects are skipped when autosync set to
875+ active-projects."""
876+ # Configure bugs2cards to sync only active-projects myproject1
877+ # and myproject2
878 self.bconf.update({"autosync": "active-projects",
879 "projects": "myproject,myproject2",
880 "sync_cards": "on", "move_cards": "on",
881 "new_lanes": "new"})
882- # Setup bug1 and bug2 in separate projects which both need to move coding -> new
883- project1 = FauxTarget(resource_type_link="project", project="myproject")
884- project2 = FauxTarget(resource_type_link="project2", project="myproject2")
885- otherproject = FauxTarget(resource_type_link="project", project="otherproject")
886- lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})
887+ # Setup bug1 and bug2 in separate projects which both need to move
888+ # coding -> new
889+ project1 = FauxTarget(
890+ resource_type_link="project", project="myproject")
891+ project2 = FauxTarget(
892+ resource_type_link="project2", project="myproject2")
893+ otherproject = FauxTarget(
894+ resource_type_link="project", project="otherproject")
895+ lp_users = FauxLaunchpadUsersForBoard(
896+ lp_to_kanban={"person1": "Person One"})
897 lp_projects = [project1, project2]
898 bug_task1 = FauxBugTask(
899 bug_id=123, target=project1, title='Test bug in myproject',
900@@ -1848,8 +1869,9 @@
901 bug_card2.type = bug_type
902 self.board.cards.extend([bug_card1, bug_card2])
903 # Include project1 and project2 in our active 'projects' fake LP data
904- lp = FauxLP({"projects": {"myproject": project1, "myproject2": project2},
905- "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
906+ lp = FauxLP({
907+ "projects": {"myproject": project1, "myproject2": project2},
908+ "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
909 sync_cards_external_ids(board, self.bconf, lp, lp_users, lp_projects)
910 coding_lane = FauxLane(board=board, path='coding')
911 self.assertEqual(new_lane, bug_card1.moved_to)
912
913=== modified file 'src/lp2kanban/tests/test_cards2workitems.py'
914--- src/lp2kanban/tests/test_cards2workitems.py 2013-03-19 16:06:09 +0000
915+++ src/lp2kanban/tests/test_cards2workitems.py 2016-09-08 22:02:56 +0000
916@@ -1,5 +1,4 @@
917 # Copyright 2012 Canonical Ltd.
918-__metaclass__ = type
919
920 import re
921 import testtools
922@@ -27,6 +26,8 @@
923 from testtools.monkey import patch
924 from textwrap import dedent
925
926+__metaclass__ = type
927+
928
929 class CardsToWorkItemsBase(testtools.TestCase):
930
931@@ -56,9 +57,9 @@
932 "String \"{string}\" did not match pattern \"{pattern}\"".format(
933 string=string, pattern=pattern))
934
935- def _generateCard(self, board=None, lane=None,
936- description_annotations=None, assigned_user_id=0,
937- title=None, external_system_url=None):
938+ def _generateCard(
939+ self, board=None, lane=None, description_annotations=None,
940+ assigned_user_id=0, title=None, external_system_url=None):
941 if board is None:
942 board = self.board
943 if lane is None:
944@@ -220,7 +221,8 @@
945 work_item = convert_card_to_work_item(
946 card, self.board, self.lp_users_for_board)
947 self.assertEqual(
948- card.description_annotations.blueprint_url, work_item.blueprint_url)
949+ card.description_annotations.blueprint_url,
950+ work_item.blueprint_url)
951
952 def test_blueprint_extracted_from_card_external_link(self):
953 # Cards can also be linked to blueprints by putting the
954@@ -233,7 +235,7 @@
955 external_system_url=self.blueprint_url,
956 description_annotations=Record(),
957 )
958- work_item = convert_card_to_work_item(
959+ convert_card_to_work_item(
960 card, self.board, self.lp_users_for_board)
961 self.assertEqual(
962 self.blueprint_url, card.description_annotations.blueprint_url)
963@@ -243,8 +245,8 @@
964 # isn't a blueprint, convert_card_to_work_item() will return
965 # None.
966 card = self._generateCard(
967- description_annotations=Record
968- (blueprint_url= 'https://nyancat.com'))
969+ description_annotations=Record(
970+ blueprint_url='https://nyancat.com'))
971 work_item = convert_card_to_work_item(
972 card, self.board, self.lp_users_for_board)
973 self.assertIsNone(work_item)
974@@ -268,8 +270,8 @@
975 self._generateCard(
976 title="Card %i" % id,
977 description_annotations=Record(
978- blueprint_url=blueprint_url_tpl % id))
979- for id in range(3)]
980+ blueprint_url=blueprint_url_tpl % id
981+ )) for id in range(3)]
982 self.work_items = convert_cards_to_work_items(
983 self.cards, self.board, self.lp_users_for_board)
984 self.blueprints = {}
985@@ -283,6 +285,7 @@
986 # We stub out the Launchpad interaction using a fake version of
987 # fetch_blueprint()
988 called_with_urls = []
989+
990 def fake_fetch_blueprint(lp, url):
991 called_with_urls.append(url)
992 return FauxBlueprint(name="somename")
993
994=== modified file 'src/lp2kanban/tests/test_description_annotations.py'
995--- src/lp2kanban/tests/test_description_annotations.py 2013-03-18 13:25:36 +0000
996+++ src/lp2kanban/tests/test_description_annotations.py 2016-09-08 22:02:56 +0000
997@@ -9,6 +9,7 @@
998 from textwrap import dedent
999 import unittest
1000
1001+
1002 class DescriptionAnnotationsTest(unittest.TestCase):
1003 """Tests for kanban.LeankitCard description annotations."""
1004
1005@@ -16,7 +17,7 @@
1006 # parse_card_description() parses key:value pairs out of a
1007 # card's description and returns them as a dict.
1008 card = LeankitCard.create(FauxLane("Test"))
1009- card.title=u"some card"
1010+ card.title = u"some card"
1011 card.description = dedent("""
1012 {"key2": "value2", "key1": "value 1"}
1013 """)
1014@@ -28,7 +29,7 @@
1015 # Any text not in the key=value form is returned unparsed by
1016 # card.parsed_description().
1017 card = LeankitCard.create(FauxLane("Test"))
1018- card.title=u"some card"
1019+ card.title = u"some card"
1020 card.description = dedent("""
1021 Some other text,
1022 {"key2": "value2", "key1": "value 1"}
1023@@ -44,7 +45,7 @@
1024 # LeankitCard.description_annotations returns only the key=value
1025 # pairs from the description field.
1026 card = LeankitCard.create(FauxLane("Test"))
1027- card.title=u"some card"
1028+ card.title = u"some card"
1029 card.description = dedent("""
1030 {"key2": "value2", "key1": "value 1"}
1031 Here's a URL: http://test.com?a=b
1032@@ -57,7 +58,7 @@
1033 # LeankitCard._setDescriptionAnnotations() can be used to write
1034 # new annotations to the description field.
1035 card = LeankitCard.create(FauxLane("Test"))
1036- card.title=u"some card"
1037+ card.title = u"some card"
1038 card.description = dedent("""
1039 Some text
1040 {"key2": "value2", "key1": "value 1"}
1041@@ -73,7 +74,7 @@
1042 # LeankitCard.description_annotations is a Record, so it hass
1043 # attribute access for its values.
1044 card = LeankitCard.create(FauxLane("Test"))
1045- card.title=u"some card"
1046+ card.title = u"some card"
1047 card.description = '{"key1": "value 1"}'
1048 self.assertIsInstance(card.description_annotations, Record)
1049 self.assertEqual("value 1", card.description_annotations.key1)
1050
1051=== modified file 'src/lp2kanban/workitems2cards.py'
1052--- src/lp2kanban/workitems2cards.py 2013-03-18 13:42:24 +0000
1053+++ src/lp2kanban/workitems2cards.py 2016-09-08 22:02:56 +0000
1054@@ -66,8 +66,8 @@
1055 assigned_user_id = assigned_user.id
1056 else:
1057 assigned_user_id = 0
1058- print "Creating card {title} [{assignee}]".format(
1059- title=work_item.text, assignee=assigned_user_id)
1060+ print(u"Creating card {title} [{assignee}]".format(
1061+ title=work_item.text, assignee=assigned_user_id))
1062 card = lane.addCard()
1063 card.title = work_item.text
1064 card.description_annotations.blueprint_url = work_item.blueprint
1065@@ -90,15 +90,15 @@
1066
1067 match = re.match(BLUEPRINT_URL_PATTERN, args.spec_url)
1068 if match is None:
1069- print "Spec URL is not a Launchpad blueprint."
1070+ print("Spec URL is not a Launchpad blueprint.")
1071 sys.exit(1)
1072
1073 if not args.dry_run:
1074- should_continue = raw_input(
1075+ should_continue = raw_input( # noqa
1076 "WARNING: This script is not well-tested and will mess with "
1077 "your Kanban board; use it at your own risk. Continue? [y/N] ")
1078 if should_continue.lower() != "y":
1079- print "Exiting."
1080+ print("Exiting.")
1081 sys.exit(0)
1082
1083 match_dict = match.groupdict()
1084@@ -115,17 +115,17 @@
1085 sys.exit(1)
1086 work_items = parse_work_items(lp, **match_dict)
1087 if not args.dry_run:
1088- should_continue = raw_input(
1089+ should_continue = raw_input( # noqa
1090 "{count} cards will be created, whether or not you want them all. "
1091 "Continue [y/N]?".format(count=len(work_items)))
1092 if args.dry_run or should_continue.lower() == "y":
1093- print "Creating cards from work items in lane {lane}.".format(
1094- lane=args.target_lane)
1095+ print(u"Creating cards from work items in lane {lane}.".format(
1096+ lane=args.target_lane))
1097 cards = work_items_to_cards(
1098 work_items, board, lane, lp_users, dry_run=args.dry_run)
1099- print "{count} cards created".format(count=len(cards))
1100+ print(u"{count} cards created").format(count=len(cards))
1101 else:
1102- print "Aborting."
1103+ print(u"Aborting.")
1104
1105 if __name__ == '__main__':
1106 main(sys.argv)

Subscribers

People subscribed via source and target branches

to all changes: