Merge lp:~cjwatson/launchpad/git-translate-trailing-path-defaults into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17417
Proposed branch: lp:~cjwatson/launchpad/git-translate-trailing-path-defaults
Merge into: lp:launchpad
Diff against target: 141 lines (+39/-14)
4 files modified
lib/lp/code/interfaces/gitlookup.py (+2/-1)
lib/lp/code/model/gitlookup.py (+21/-9)
lib/lp/code/model/tests/test_gitlookup.py (+11/-2)
lib/lp/registry/browser/person.py (+5/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-translate-trailing-path-defaults
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+254587@code.launchpad.net

Commit message

Make GitLookup.getByPath handle trailing path segments on objects which are also traversable.

Description of the change

Make GitLookup.getByPath handle trailing path segments on objects which are also traversable.

This fixes a case I missed in https://code.launchpad.net/~cjwatson/launchpad/git-translate-trailing-path/+merge/254551: for example, for cgit support, /PROJECT/commit/?id=sha1 needs to be translatable to the default repository for PROJECT with some trailing path information, even though the traversal code knows how to traverse from projects. This requires some adjustment of internal interfaces since we may have to push back a segment.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/interfaces/gitlookup.py'
--- lib/lp/code/interfaces/gitlookup.py 2015-03-30 10:00:48 +0000
+++ lib/lp/code/interfaces/gitlookup.py 2015-03-30 14:55:25 +0000
@@ -53,7 +53,8 @@
53 :return: A tuple of::53 :return: A tuple of::
54 * an `IPerson`, or None;54 * an `IPerson`, or None;
55 * an `IHasGitRepositories`;55 * an `IHasGitRepositories`;
56 * an `IGitRepository`, or None.56 * an `IGitRepository`, or None;
57 * a trailing path segment, or None.
57 """58 """
5859
59 def traverse_path(path):60 def traverse_path(path):
6061
=== modified file 'lib/lp/code/model/gitlookup.py'
--- lib/lp/code/model/gitlookup.py 2015-03-30 09:46:03 +0000
+++ lib/lp/code/model/gitlookup.py 2015-03-30 14:55:25 +0000
@@ -261,26 +261,34 @@
261 else:261 else:
262 target = owner262 target = owner
263 traversable = adapt(owner, IGitTraversable)263 traversable = adapt(owner, IGitTraversable)
264 trailing = None
264 segments_iter = SegmentIterator(segments)265 segments_iter = SegmentIterator(segments)
265 while traversable is not None:266 while traversable is not None:
266 try:267 try:
267 name = next(segments_iter)268 name = next(segments_iter)
268 except StopIteration:269 except StopIteration:
269 break270 break
270 owner, target, repository = traversable.traverse(271 try:
271 owner, name, segments_iter)272 owner, target, repository = traversable.traverse(
273 owner, name, segments_iter)
274 except InvalidNamespace:
275 if target is not None or repository is not None:
276 # We have some information, so the rest may consist of
277 # trailing path information.
278 trailing = name
279 break
272 if repository is not None:280 if repository is not None:
273 break281 break
274 traversable = adapt(target, IGitTraversable)282 traversable = adapt(target, IGitTraversable)
275 if target is None or not IHasGitRepositories.providedBy(target):283 if target is None or not IHasGitRepositories.providedBy(target):
276 raise InvalidNamespace("/".join(segments_iter.traversed))284 raise InvalidNamespace("/".join(segments_iter.traversed))
277 return owner, target, repository285 return owner, target, repository, trailing
278286
279 def traverse_path(self, path):287 def traverse_path(self, path):
280 """See `IGitTraverser`."""288 """See `IGitTraverser`."""
281 segments = iter(path.split("/"))289 segments = iter(path.split("/"))
282 owner, target, repository = self.traverse(segments)290 owner, target, repository, trailing = self.traverse(segments)
283 if list(segments):291 if trailing or list(segments):
284 raise InvalidNamespace(path)292 raise InvalidNamespace(path)
285 return owner, target, repository293 return owner, target, repository
286294
@@ -340,9 +348,10 @@
340 """See `IGitLookup`."""348 """See `IGitLookup`."""
341 try:349 try:
342 if unique_name.startswith("~"):350 if unique_name.startswith("~"):
351 traverser = getUtility(IGitTraverser)
343 segments = iter(unique_name.split("/"))352 segments = iter(unique_name.split("/"))
344 _, _, repository = getUtility(IGitTraverser).traverse(segments)353 _, _, repository, trailing = traverser.traverse(segments)
345 if repository is None or list(segments):354 if repository is None or trailing or list(segments):
346 raise InvalidNamespace(unique_name)355 raise InvalidNamespace(unique_name)
347 return repository356 return repository
348 except (InvalidNamespace, NameLookupFailed):357 except (InvalidNamespace, NameLookupFailed):
@@ -354,7 +363,7 @@
354 traverser = getUtility(IGitTraverser)363 traverser = getUtility(IGitTraverser)
355 segments = iter(path.split("/"))364 segments = iter(path.split("/"))
356 try:365 try:
357 owner, target, repository = traverser.traverse(segments)366 owner, target, repository, trailing = traverser.traverse(segments)
358 except (InvalidNamespace, InvalidProductName, NameLookupFailed):367 except (InvalidNamespace, InvalidProductName, NameLookupFailed):
359 return None, None368 return None, None
360 if repository is None:369 if repository is None:
@@ -366,4 +375,7 @@
366 else:375 else:
367 repository = repository_set.getDefaultRepositoryForOwner(376 repository = repository_set.getDefaultRepositoryForOwner(
368 owner, target)377 owner, target)
369 return repository, "/".join(segments)378 trailing_segments = list(segments)
379 if trailing:
380 trailing_segments.insert(0, trailing)
381 return repository, "/".join(trailing_segments)
370382
=== modified file 'lib/lp/code/model/tests/test_gitlookup.py'
--- lib/lp/code/model/tests/test_gitlookup.py 2015-03-30 09:46:03 +0000
+++ lib/lp/code/model/tests/test_gitlookup.py 2015-03-30 14:55:25 +0000
@@ -137,6 +137,15 @@
137 (repository, "foo/bar"),137 (repository, "foo/bar"),
138 self.lookup.getByPath("%s/foo/bar" % repository.unique_name))138 self.lookup.getByPath("%s/foo/bar" % repository.unique_name))
139139
140 def test_default_extra_path(self):
141 repository = self.factory.makeGitRepository()
142 with person_logged_in(repository.target.owner):
143 getUtility(IGitRepositorySet).setDefaultRepository(
144 repository.target, repository)
145 self.assertEqual(
146 (repository, "foo/bar"),
147 self.lookup.getByPath("%s/foo/bar" % repository.shortened_path))
148
140 def test_invalid_namespace(self):149 def test_invalid_namespace(self):
141 # If `getByPath` is given a path to something with no default Git150 # If `getByPath` is given a path to something with no default Git
142 # repository, such as a distribution, it returns (None, _).151 # repository, such as a distribution, it returns (None, _).
@@ -502,8 +511,8 @@
502 owner=person, target=person, name=u"repository")511 owner=person, target=person, name=u"repository")
503 segments = ["~person", "+git", "repository"]512 segments = ["~person", "+git", "repository"]
504 self.assertEqual(513 self.assertEqual(
505 (person, person, repository),514 (person, person, repository, None),
506 self.traverser.traverse(iter(segments)))515 self.traverser.traverse(iter(segments)))
507 self.assertEqual(516 self.assertEqual(
508 (person, person, repository),517 (person, person, repository, None),
509 self.traverser.traverse(iter(segments[1:]), owner=person))518 self.traverser.traverse(iter(segments[1:]), owner=person))
510519
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2015-03-17 16:22:56 +0000
+++ lib/lp/registry/browser/person.py 2015-03-30 14:55:25 +0000
@@ -375,12 +375,15 @@
375 num_segments = len(segments)375 num_segments = len(segments)
376 iter_segments = iter(segments)376 iter_segments = iter(segments)
377 traverser = getUtility(IGitTraverser)377 traverser = getUtility(IGitTraverser)
378 _, target, repository = traverser.traverse(378 _, target, repository, trailing = traverser.traverse(
379 iter_segments, owner=self.context)379 iter_segments, owner=self.context)
380 if repository is None:380 if repository is None:
381 raise NotFoundError381 raise NotFoundError
382 # Subtract one because the pillar has already been traversed.382 # Subtract one because the pillar has already been traversed.
383 for _ in range(num_segments - len(list(iter_segments)) - 1):383 num_traversed = num_segments - len(list(iter_segments)) - 1
384 if trailing:
385 num_traversed -= 1
386 for _ in range(num_traversed):
384 self.request.stepstogo.consume()387 self.request.stepstogo.consume()
385388
386 if IProduct.providedBy(target):389 if IProduct.providedBy(target):