Merge lp:~jelmer/brz/location-purpose into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/location-purpose
Merge into: lp:brz
Diff against target: 383 lines (+87/-27)
14 files modified
breezy/builtins.py (+4/-4)
breezy/directory_service.py (+26/-6)
breezy/git/directory.py (+1/-1)
breezy/location.py (+6/-4)
breezy/plugins/launchpad/__init__.py (+0/-1)
breezy/plugins/launchpad/lp_directory.py (+1/-1)
breezy/plugins/launchpad/test_lp_directory.py (+1/-1)
breezy/push.py (+1/-1)
breezy/tests/blackbox/test_pull.py (+1/-1)
breezy/tests/blackbox/test_switch.py (+1/-1)
breezy/tests/test_bundle.py (+1/-1)
breezy/tests/test_directory_service.py (+36/-1)
breezy/tests/test_location.py (+2/-2)
breezy/transport/__init__.py (+6/-2)
To merge this branch: bzr merge lp:~jelmer/brz/location-purpose
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+362940@code.launchpad.net

Commit message

Add a purpose argument to location dereference functions.

Description of the change

Add a purpose argument to location dereference functions.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

This branch has the hook change in as well. Not quite sure I understand the reasoning here, but seems basically reasonable? Tests use 'push' as a purpose, but the documented values are 'read' 'write' None?

review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Change push => write

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

The idea here is that you can declare whether you're going to read from or write to a branch, allowing callees (such as a directory service) to e.g. give you a https or bzr+ssh URL.

Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'breezy/builtins.py'
--- breezy/builtins.py 2019-02-04 01:01:24 +0000
+++ breezy/builtins.py 2019-02-14 22:08:33 +0000
@@ -1480,7 +1480,7 @@
1480 revision_id = br_from.last_revision()1480 revision_id = br_from.last_revision()
1481 if to_location is None:1481 if to_location is None:
1482 to_location = urlutils.derive_to_location(from_location)1482 to_location = urlutils.derive_to_location(from_location)
1483 to_transport = transport.get_transport(to_location)1483 to_transport = transport.get_transport(to_location, purpose='write')
1484 try:1484 try:
1485 to_transport.mkdir('.')1485 to_transport.mkdir('.')
1486 except errors.FileExists:1486 except errors.FileExists:
@@ -1574,7 +1574,7 @@
15741574
1575 def run(self, location=".", recursive=False):1575 def run(self, location=".", recursive=False):
1576 if recursive:1576 if recursive:
1577 t = transport.get_transport(location)1577 t = transport.get_transport(location, purpose='read')
1578 if not t.listable():1578 if not t.listable():
1579 raise errors.BzrCommandError(1579 raise errors.BzrCommandError(
1580 "Can't scan this type of location.")1580 "Can't scan this type of location.")
@@ -2058,7 +2058,7 @@
2058 if location is None:2058 if location is None:
2059 location = u'.'2059 location = u'.'
20602060
2061 to_transport = transport.get_transport(location)2061 to_transport = transport.get_transport(location, purpose='write')
20622062
2063 # The path has to exist to initialize a2063 # The path has to exist to initialize a
2064 # branch inside of it.2064 # branch inside of it.
@@ -2178,7 +2178,7 @@
2178 if location is None:2178 if location is None:
2179 location = '.'2179 location = '.'
21802180
2181 to_transport = transport.get_transport(location)2181 to_transport = transport.get_transport(location, purpose='write')
21822182
2183 if format.fixed_components:2183 if format.fixed_components:
2184 repo_format_name = None2184 repo_format_name = None
21852185
=== modified file 'breezy/directory_service.py'
--- breezy/directory_service.py 2018-11-11 04:08:32 +0000
+++ breezy/directory_service.py 2019-02-14 22:08:33 +0000
@@ -67,7 +67,7 @@
67 name and URL, and return a URL.67 name and URL, and return a URL.
68 """68 """
6969
70 def dereference(self, url):70 def dereference(self, url, purpose=None):
71 """Dereference a supplied URL if possible.71 """Dereference a supplied URL if possible.
7272
73 URLs that match a registered directory service prefix are looked up in73 URLs that match a registered directory service prefix are looked up in
@@ -77,19 +77,39 @@
77 requires further dereferencing.77 requires further dereferencing.
7878
79 :param url: The URL to dereference79 :param url: The URL to dereference
80 :param purpose: Purpose of the URL ('read', 'write' or None - if not declared)
80 :return: The dereferenced URL if applicable, the input URL otherwise.81 :return: The dereferenced URL if applicable, the input URL otherwise.
81 """82 """
82 match = self.get_prefix(url)83 match = self.get_prefix(url)
83 if match is None:84 if match is None:
84 return url85 return url
85 service, name = match86 service, name = match
86 return service().look_up(name, url)87 directory = service()
88 try:
89 return directory.look_up(name, url, purpose=purpose)
90 except TypeError:
91 # Compatibility for plugins written for Breezy < 3.0.0
92 return directory.look_up(name, url)
8793
8894
89directories = DirectoryServiceRegistry()95directories = DirectoryServiceRegistry()
9096
9197
92class AliasDirectory(object):98class Directory(object):
99 """Abstract directory lookup class."""
100
101 def look_up(self, name, url, purpose=None):
102 """Look up an entry in a directory.
103
104 :param name: Directory name
105 :param url: The URL to dereference
106 :param purpose: Purpose of the URL ('read', 'write' or None - if not declared)
107 :return: The dereferenced URL if applicable, the input URL otherwise.
108 """
109 raise NotImplementedError(self.look_up)
110
111
112class AliasDirectory(Directory):
93 """Directory lookup for locations associated with a branch.113 """Directory lookup for locations associated with a branch.
94114
95 :parent, :submit, :public, :push, :this, and :bound are currently115 :parent, :submit, :public, :push, :this, and :bound are currently
@@ -110,7 +130,7 @@
110 branch_aliases.register('this', lambda b: b.base,130 branch_aliases.register('this', lambda b: b.base,
111 help="This branch.")131 help="This branch.")
112132
113 def look_up(self, name, url):133 def look_up(self, name, url, purpose=None):
114 branch = _mod_branch.Branch.open_containing('.')[0]134 branch = _mod_branch.Branch.open_containing('.')[0]
115 parts = url.split('/', 1)135 parts = url.split('/', 1)
116 if len(parts) == 2:136 if len(parts) == 2:
@@ -156,14 +176,14 @@
156 'Easy access to remembered branch locations')176 'Easy access to remembered branch locations')
157177
158178
159class ColocatedDirectory(object):179class ColocatedDirectory(Directory):
160 """Directory lookup for colocated branches.180 """Directory lookup for colocated branches.
161181
162 co:somename will resolve to the colocated branch with "somename" in182 co:somename will resolve to the colocated branch with "somename" in
163 the current directory.183 the current directory.
164 """184 """
165185
166 def look_up(self, name, url):186 def look_up(self, name, url, purpose=None):
167 dir = _mod_controldir.ControlDir.open_containing('.')[0]187 dir = _mod_controldir.ControlDir.open_containing('.')[0]
168 return urlutils.join_segment_parameters(dir.user_url,188 return urlutils.join_segment_parameters(dir.user_url,
169 {"branch": urlutils.escape(name)})189 {"branch": urlutils.escape(name)})
170190
=== modified file 'breezy/git/directory.py'
--- breezy/git/directory.py 2018-11-11 04:08:32 +0000
+++ breezy/git/directory.py 2019-02-14 22:08:33 +0000
@@ -26,6 +26,6 @@
2626
27class GitHubDirectory(object):27class GitHubDirectory(object):
2828
29 def look_up(self, name, url):29 def look_up(self, name, url, purpose=None):
30 """See DirectoryService.look_up"""30 """See DirectoryService.look_up"""
31 return "git+ssh://git@github.com/" + name31 return "git+ssh://git@github.com/" + name
3232
=== modified file 'breezy/location.py'
--- breezy/location.py 2019-02-09 02:03:11 +0000
+++ breezy/location.py 2019-02-14 22:08:33 +0000
@@ -37,26 +37,28 @@
37 Hooks.__init__(self, "breezy.location", "hooks")37 Hooks.__init__(self, "breezy.location", "hooks")
38 self.add_hook(38 self.add_hook(
39 'rewrite_url',39 'rewrite_url',
40 "Called with a URL to rewrite.", (3, 0))40 "Possibly rewrite a URL. Called with a URL to rewrite and the "
41 "purpose of the URL.", (3, 0))
4142
4243
43hooks = LocationHooks()44hooks = LocationHooks()
4445
4546
46def location_to_url(location):47def location_to_url(location, purpose=None):
47 """Determine a fully qualified URL from a location string.48 """Determine a fully qualified URL from a location string.
4849
49 This will try to interpret location as both a URL and a directory path. It50 This will try to interpret location as both a URL and a directory path. It
50 will also lookup the location in directories.51 will also lookup the location in directories.
5152
52 :param location: Unicode or byte string object with a location53 :param location: Unicode or byte string object with a location
54 :param purpose: Intended method of access (None, 'read' or 'write')
53 :raise InvalidURL: If the location is already a URL, but not valid.55 :raise InvalidURL: If the location is already a URL, but not valid.
54 :return: Byte string with resulting URL56 :return: Byte string with resulting URL
55 """57 """
56 if not isinstance(location, string_types):58 if not isinstance(location, string_types):
57 raise AssertionError("location not a byte or unicode string")59 raise AssertionError("location not a byte or unicode string")
58 from .directory_service import directories60 from .directory_service import directories
59 location = directories.dereference(location)61 location = directories.dereference(location, purpose)
6062
61 # Catch any URLs which are passing Unicode rather than ASCII63 # Catch any URLs which are passing Unicode rather than ASCII
62 try:64 try:
@@ -77,6 +79,6 @@
77 return urlutils.local_path_to_url(location)79 return urlutils.local_path_to_url(location)
7880
79 for hook in hooks['rewrite_url']:81 for hook in hooks['rewrite_url']:
80 location = hook(location)82 location = hook(location, purpose=purpose)
8183
82 return location84 return location
8385
=== modified file 'breezy/plugins/launchpad/__init__.py'
--- breezy/plugins/launchpad/__init__.py 2019-02-02 22:23:40 +0000
+++ breezy/plugins/launchpad/__init__.py 2019-02-14 22:08:33 +0000
@@ -29,7 +29,6 @@
2929
30 launchpad-login: Show or set the Launchpad user ID30 launchpad-login: Show or set the Launchpad user ID
31 launchpad-open: Open a Launchpad branch page in your web browser31 launchpad-open: Open a Launchpad branch page in your web browser
32 launchpad-mirror: Ask Launchpad to mirror a branch now
3332
34As well as the following deprecated command:33As well as the following deprecated command:
3534
3635
=== modified file 'breezy/plugins/launchpad/lp_directory.py'
--- breezy/plugins/launchpad/lp_directory.py 2019-02-02 22:23:40 +0000
+++ breezy/plugins/launchpad/lp_directory.py 2019-02-14 22:08:33 +0000
@@ -117,7 +117,7 @@
117117
118class LaunchpadDirectory(object):118class LaunchpadDirectory(object):
119119
120 def look_up(self, name, url):120 def look_up(self, name, url, purpose=None):
121 """See DirectoryService.look_up"""121 """See DirectoryService.look_up"""
122 return self._resolve(url)122 return self._resolve(url)
123123
124124
=== modified file 'breezy/plugins/launchpad/test_lp_directory.py'
--- breezy/plugins/launchpad/test_lp_directory.py 2019-01-17 01:04:56 +0000
+++ breezy/plugins/launchpad/test_lp_directory.py 2019-02-14 22:08:33 +0000
@@ -397,7 +397,7 @@
397 class FooService(object):397 class FooService(object):
398 """A directory service that maps the name to a FILE url"""398 """A directory service that maps the name to a FILE url"""
399399
400 def look_up(self, name, url):400 def look_up(self, name, url, purpose=None):
401 if 'lp:///apt' == url:401 if 'lp:///apt' == url:
402 return target_branch.base.rstrip('/')402 return target_branch.base.rstrip('/')
403 return '!unexpected look_up value!'403 return '!unexpected look_up value!'
404404
=== modified file 'breezy/push.py'
--- breezy/push.py 2018-11-17 16:53:10 +0000
+++ breezy/push.py 2019-02-14 22:08:33 +0000
@@ -81,7 +81,7 @@
81 directory exists without a current control directory in it81 directory exists without a current control directory in it
82 :param lossy: Allow lossy push82 :param lossy: Allow lossy push
83 """83 """
84 to_transport = transport.get_transport(location)84 to_transport = transport.get_transport(location, purpose='write')
85 try:85 try:
86 dir_to = controldir.ControlDir.open_from_transport(to_transport)86 dir_to = controldir.ControlDir.open_from_transport(to_transport)
87 except errors.NotBranchError:87 except errors.NotBranchError:
8888
=== modified file 'breezy/tests/blackbox/test_pull.py'
--- breezy/tests/blackbox/test_pull.py 2018-11-29 23:42:41 +0000
+++ breezy/tests/blackbox/test_pull.py 2019-02-14 22:08:33 +0000
@@ -353,7 +353,7 @@
353 class FooService(object):353 class FooService(object):
354 """A directory service that always returns source"""354 """A directory service that always returns source"""
355355
356 def look_up(self, name, url):356 def look_up(self, name, url, purpose=None):
357 return 'source'357 return 'source'
358 directories.register('foo:', FooService, 'Testing directory service')358 directories.register('foo:', FooService, 'Testing directory service')
359 self.addCleanup(directories.remove, 'foo:')359 self.addCleanup(directories.remove, 'foo:')
360360
=== modified file 'breezy/tests/blackbox/test_switch.py'
--- breezy/tests/blackbox/test_switch.py 2019-01-04 21:21:12 +0000
+++ breezy/tests/blackbox/test_switch.py 2019-02-14 22:08:33 +0000
@@ -347,7 +347,7 @@
347 tree = branch.create_checkout('tree', lightweight=True)347 tree = branch.create_checkout('tree', lightweight=True)
348348
349 class FooLookup(object):349 class FooLookup(object):
350 def look_up(self, name, url):350 def look_up(self, name, url, purpose=None):
351 return 'foo-' + name351 return 'foo-' + name
352 directories.register('foo:', FooLookup, 'Create branches named foo-')352 directories.register('foo:', FooLookup, 'Create branches named foo-')
353 self.addCleanup(directories.remove, 'foo:')353 self.addCleanup(directories.remove, 'foo:')
354354
=== modified file 'breezy/tests/test_bundle.py'
--- breezy/tests/test_bundle.py 2019-01-01 21:23:40 +0000
+++ breezy/tests/test_bundle.py 2019-02-14 22:08:33 +0000
@@ -1848,7 +1848,7 @@
1848 class FooService(object):1848 class FooService(object):
1849 """A directory service that always returns source"""1849 """A directory service that always returns source"""
18501850
1851 def look_up(self, name, url):1851 def look_up(self, name, url, purpose=None):
1852 return 'source'1852 return 'source'
1853 directories.register('foo:', FooService, 'Testing directory service')1853 directories.register('foo:', FooService, 'Testing directory service')
1854 self.addCleanup(directories.remove, 'foo:')1854 self.addCleanup(directories.remove, 'foo:')
18551855
=== modified file 'breezy/tests/test_directory_service.py'
--- breezy/tests/test_directory_service.py 2018-11-11 04:08:32 +0000
+++ breezy/tests/test_directory_service.py 2019-02-14 22:08:33 +0000
@@ -36,7 +36,7 @@
36 # eg 'file:///foo' on Unix, or 'file:///C:/foo' on Windows36 # eg 'file:///foo' on Unix, or 'file:///C:/foo' on Windows
37 base = urlutils.local_path_to_url('/foo')37 base = urlutils.local_path_to_url('/foo')
3838
39 def look_up(self, name, url):39 def look_up(self, name, url, purpose=None):
40 return self.base + name40 return self.base + name
4141
4242
@@ -55,7 +55,12 @@
55 def test_dereference(self):55 def test_dereference(self):
56 self.assertEqual(FooService.base + 'bar',56 self.assertEqual(FooService.base + 'bar',
57 self.registry.dereference('foo:bar'))57 self.registry.dereference('foo:bar'))
58 self.assertEqual(FooService.base + 'bar',
59 self.registry.dereference('foo:bar', purpose='write'))
58 self.assertEqual('baz:qux', self.registry.dereference('baz:qux'))60 self.assertEqual('baz:qux', self.registry.dereference('baz:qux'))
61 self.assertEqual(
62 'baz:qux',
63 self.registry.dereference('baz:qux', purpose='write'))
5964
60 def test_get_transport(self):65 def test_get_transport(self):
61 directories.register('foo:', FooService, 'Map foo URLs to http urls')66 directories.register('foo:', FooService, 'Map foo URLs to http urls')
@@ -64,6 +69,36 @@
64 transport.get_transport('foo:bar').base)69 transport.get_transport('foo:bar').base)
6570
6671
72class OldService(object):
73 """A directory service that maps the name to a FILE url"""
74
75 # eg 'file:///foo' on Unix, or 'file:///C:/foo' on Windows
76 base = urlutils.local_path_to_url('/foo')
77
78 def look_up(self, name, url):
79 return self.base + name
80
81
82class TestOldDirectoryLookup(TestCase):
83 """Test compatibility with older implementations of Directory
84 that don't support the purpose argument."""
85
86 def setUp(self):
87 super(TestOldDirectoryLookup, self).setUp()
88 self.registry = DirectoryServiceRegistry()
89 self.registry.register('old:', OldService, 'Map foo URLs to http urls')
90
91 def test_dereference(self):
92 self.assertEqual(OldService.base + 'bar',
93 self.registry.dereference('old:bar'))
94 self.assertEqual(OldService.base + 'bar',
95 self.registry.dereference('old:bar', purpose='write'))
96 self.assertEqual('baz:qux', self.registry.dereference('baz:qux'))
97 self.assertEqual(
98 'baz:qux',
99 self.registry.dereference('baz:qux', purpose='write'))
100
101
67class TestAliasDirectory(TestCaseWithTransport):102class TestAliasDirectory(TestCaseWithTransport):
68103
69 def setUp(self):104 def setUp(self):
70105
=== modified file 'breezy/tests/test_location.py'
--- breezy/tests/test_location.py 2019-02-09 02:03:11 +0000
+++ breezy/tests/test_location.py 2019-02-14 22:08:33 +0000
@@ -30,7 +30,7 @@
3030
31class SomeDirectory(object):31class SomeDirectory(object):
3232
33 def look_up(self, name, url):33 def look_up(self, name, url, purpose=None):
34 return "http://bar"34 return "http://bar"
3535
3636
@@ -78,7 +78,7 @@
78 def test_rewrite_hook(self):78 def test_rewrite_hook(self):
79 self.assertEqual(79 self.assertEqual(
80 'http://foo.example.com/blah', location_to_url('http://foo.example.com/blah'))80 'http://foo.example.com/blah', location_to_url('http://foo.example.com/blah'))
81 def rewrite_url(url):81 def rewrite_url(url, purpose=None):
82 return url.replace('foo', 'bar')82 return url.replace('foo', 'bar')
83 self.addCleanup(location_hooks.uninstall_named_hook, 'rewrite_url', 'test')83 self.addCleanup(location_hooks.uninstall_named_hook, 'rewrite_url', 'test')
84 location_hooks.install_named_hook('rewrite_url', rewrite_url, 'test')84 location_hooks.install_named_hook('rewrite_url', rewrite_url, 'test')
8585
=== modified file 'breezy/transport/__init__.py'
--- breezy/transport/__init__.py 2018-12-10 05:22:17 +0000
+++ breezy/transport/__init__.py 2019-02-14 22:08:33 +0000
@@ -1553,7 +1553,7 @@
1553 raise errors.UnsupportedProtocol(url, last_err)1553 raise errors.UnsupportedProtocol(url, last_err)
15541554
15551555
1556def get_transport(base, possible_transports=None):1556def get_transport(base, possible_transports=None, purpose=None):
1557 """Open a transport to access a URL or directory.1557 """Open a transport to access a URL or directory.
15581558
1559 :param base: either a URL or a directory name.1559 :param base: either a URL or a directory name.
@@ -1561,13 +1561,17 @@
1561 :param transports: optional reusable transports list. If not None, created1561 :param transports: optional reusable transports list. If not None, created
1562 transports will be added to the list.1562 transports will be added to the list.
15631563
1564 :param purpose: Purpose for which the transport will be used
1565 (e.g. 'read', 'write' or None)
1566
1564 :return: A new transport optionally sharing its connection with one of1567 :return: A new transport optionally sharing its connection with one of
1565 possible_transports.1568 possible_transports.
1566 """1569 """
1567 if base is None:1570 if base is None:
1568 base = '.'1571 base = '.'
1569 return get_transport_from_url(1572 return get_transport_from_url(
1570 _mod_location.location_to_url(base), possible_transports)1573 _mod_location.location_to_url(base, purpose=purpose),
1574 possible_transports)
15711575
15721576
1573def _try_transport_factories(base, factory_list):1577def _try_transport_factories(base, factory_list):

Subscribers

People subscribed via source and target branches