Merge lp:~jelmer/brz/move-mergeable 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/move-mergeable
Merge into: lp:brz
Diff against target: 475 lines (+207/-148)
8 files modified
breezy/builtins.py (+5/-5)
breezy/bundle/__init__.py (+0/-72)
breezy/bundle/commands.py (+1/-1)
breezy/mergeable.py (+107/-0)
breezy/tests/__init__.py (+1/-0)
breezy/tests/test_bundle.py (+0/-68)
breezy/tests/test_mergeable.py (+91/-0)
breezy/tests/test_read_bundle.py (+2/-2)
To merge this branch: bzr merge lp:~jelmer/brz/move-mergeable
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+358676@code.launchpad.net

Commit message

Split mergeable bits out of breezy.bundle.

Description of the change

Split mergeable bits out of breezy.bundle.

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

Thanks!

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
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-05-28 21:53:23 +0000
+++ breezy/builtins.py 2019-05-29 03:29:58 +0000
@@ -33,7 +33,6 @@
33from breezy import (33from breezy import (
34 branch as _mod_branch,34 branch as _mod_branch,
35 bugtracker,35 bugtracker,
36 bundle,
37 cache_utf8,36 cache_utf8,
38 controldir,37 controldir,
39 directory_service,38 directory_service,
@@ -46,6 +45,7 @@
46 lazy_regex,45 lazy_regex,
47 log,46 log,
48 merge as _mod_merge,47 merge as _mod_merge,
48 mergeable as _mod_mergeable,
49 merge_directive,49 merge_directive,
50 osutils,50 osutils,
51 reconfigure,51 reconfigure,
@@ -1210,8 +1210,8 @@
1210 possible_transports = []1210 possible_transports = []
1211 if location is not None:1211 if location is not None:
1212 try:1212 try:
1213 mergeable = bundle.read_mergeable_from_url(location,1213 mergeable = _mod_mergeable.read_mergeable_from_url(
1214 possible_transports=possible_transports)1214 location, possible_transports=possible_transports)
1215 except errors.NotABundle:1215 except errors.NotABundle:
1216 mergeable = None1216 mergeable = None
12171217
@@ -4438,8 +4438,8 @@
4438 self.add_cleanup(tree.lock_write().unlock)4438 self.add_cleanup(tree.lock_write().unlock)
4439 if location is not None:4439 if location is not None:
4440 try:4440 try:
4441 mergeable = bundle.read_mergeable_from_url(location,4441 mergeable = _mod_mergeable.read_mergeable_from_url(
4442 possible_transports=possible_transports)4442 location, possible_transports=possible_transports)
4443 except errors.NotABundle:4443 except errors.NotABundle:
4444 mergeable = None4444 mergeable = None
4445 else:4445 else:
44464446
=== modified file 'breezy/bundle/__init__.py'
--- breezy/bundle/__init__.py 2018-11-11 04:08:32 +0000
+++ breezy/bundle/__init__.py 2019-05-29 03:29:58 +0000
@@ -15,75 +15,3 @@
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1616
17from __future__ import absolute_import17from __future__ import absolute_import
18
19
20from ..lazy_import import lazy_import
21lazy_import(globals(), """
22from breezy import (
23 errors,
24 transport as _mod_transport,
25 urlutils,
26 )
27from breezy.bundle import serializer as _serializer
28from breezy.merge_directive import MergeDirective
29from breezy.i18n import gettext
30""")
31from ..sixish import (
32 BytesIO,
33 )
34from ..trace import note
35
36
37def read_mergeable_from_url(url, _do_directive=True, possible_transports=None):
38 """Read mergable object from a given URL.
39
40 :return: An object supporting get_target_revision. Raises NotABundle if
41 the target is not a mergeable type.
42 """
43 child_transport = _mod_transport.get_transport(url,
44 possible_transports=possible_transports)
45 transport = child_transport.clone('..')
46 filename = transport.relpath(child_transport.base)
47 mergeable, transport = read_mergeable_from_transport(transport, filename,
48 _do_directive)
49 return mergeable
50
51
52def read_mergeable_from_transport(transport, filename, _do_directive=True):
53 def get_bundle(transport):
54 return BytesIO(transport.get_bytes(filename)), transport
55
56 def redirected_transport(transport, exception, redirection_notice):
57 note(redirection_notice)
58 url, filename = urlutils.split(exception.target,
59 exclude_trailing_slash=False)
60 if not filename:
61 raise errors.NotABundle(gettext('A directory cannot be a bundle'))
62 return _mod_transport.get_transport_from_url(url)
63
64 try:
65 bytef, transport = _mod_transport.do_catching_redirections(
66 get_bundle, transport, redirected_transport)
67 except errors.TooManyRedirections:
68 raise errors.NotABundle(transport.clone(filename).base)
69 except (errors.ConnectionReset, errors.ConnectionError) as e:
70 raise
71 except (errors.TransportError, errors.PathError) as e:
72 raise errors.NotABundle(str(e))
73 except (IOError,) as e:
74 # jam 20060707
75 # Abstraction leakage, SFTPTransport.get('directory')
76 # doesn't always fail at get() time. Sometimes it fails
77 # during read. And that raises a generic IOError with
78 # just the string 'Failure'
79 # StubSFTPServer does fail during get() (because of prefetch)
80 # so it has an opportunity to translate the error.
81 raise errors.NotABundle(str(e))
82
83 if _do_directive:
84 try:
85 return MergeDirective.from_lines(bytef), transport
86 except errors.NotAMergeDirective:
87 bytef.seek(0)
88
89 return _serializer.read_bundle(bytef), transport
9018
=== modified file 'breezy/bundle/commands.py'
--- breezy/bundle/commands.py 2018-11-11 04:08:32 +0000
+++ breezy/bundle/commands.py 2019-05-29 03:29:58 +0000
@@ -53,7 +53,7 @@
5353
54 def run(self, location, verbose=False):54 def run(self, location, verbose=False):
55 from breezy.bundle.serializer import read_bundle55 from breezy.bundle.serializer import read_bundle
56 from breezy.bundle import read_mergeable_from_url56 from breezy.mergeable import read_mergeable_from_url
57 from breezy import osutils57 from breezy import osutils
58 term_encoding = osutils.get_terminal_encoding()58 term_encoding = osutils.get_terminal_encoding()
59 bundle_info = read_mergeable_from_url(location)59 bundle_info = read_mergeable_from_url(location)
6060
=== added file 'breezy/mergeable.py'
--- breezy/mergeable.py 1970-01-01 00:00:00 +0000
+++ breezy/mergeable.py 2019-05-29 03:29:58 +0000
@@ -0,0 +1,107 @@
1# Copyright (C) 2005-2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17from __future__ import absolute_import
18
19from .lazy_import import lazy_import
20lazy_import(globals(), """
21from breezy import (
22 errors,
23 transport as _mod_transport,
24 urlutils,
25 )
26from breezy.bundle import serializer as _serializer
27from breezy.merge_directive import MergeDirective
28from breezy.i18n import gettext
29""")
30
31from .sixish import (
32 BytesIO,
33 )
34from .trace import note
35
36
37class Mergeable(object):
38 """A mergeable object."""
39
40 def install_revisions(self, repository):
41 """Install the data from this mergeable into the specified repository.
42
43 :param repository: Repository
44 """
45 raise NotImplementedError(self.install_revisions)
46
47 def get_merge_request(self, repository):
48 """Extract merge request data.
49
50 :return: tuple with (base_revision_id, target_revision_id, verified)
51 """
52 raise NotImplementedError(self.get_merge_request)
53
54
55def read_mergeable_from_url(url, _do_directive=True, possible_transports=None):
56 """Read mergable object from a given URL.
57
58 :return: An object supporting get_target_revision. Raises NotABundle if
59 the target is not a mergeable type.
60 """
61 child_transport = _mod_transport.get_transport(
62 url, possible_transports=possible_transports)
63 transport = child_transport.clone('..')
64 filename = transport.relpath(child_transport.base)
65 mergeable, transport = read_mergeable_from_transport(transport, filename,
66 _do_directive)
67 return mergeable
68
69
70def read_mergeable_from_transport(transport, filename, _do_directive=True):
71 def get_bundle(transport):
72 return BytesIO(transport.get_bytes(filename)), transport
73
74 def redirected_transport(transport, exception, redirection_notice):
75 note(redirection_notice)
76 url, filename = urlutils.split(exception.target,
77 exclude_trailing_slash=False)
78 if not filename:
79 raise errors.NotABundle(gettext('A directory cannot be a bundle'))
80 return _mod_transport.get_transport_from_url(url)
81
82 try:
83 bytef, transport = _mod_transport.do_catching_redirections(
84 get_bundle, transport, redirected_transport)
85 except errors.TooManyRedirections:
86 raise errors.NotABundle(transport.clone(filename).base)
87 except (errors.ConnectionReset, errors.ConnectionError) as e:
88 raise
89 except (errors.TransportError, errors.PathError) as e:
90 raise errors.NotABundle(str(e))
91 except (IOError,) as e:
92 # jam 20060707
93 # Abstraction leakage, SFTPTransport.get('directory')
94 # doesn't always fail at get() time. Sometimes it fails
95 # during read. And that raises a generic IOError with
96 # just the string 'Failure'
97 # StubSFTPServer does fail during get() (because of prefetch)
98 # so it has an opportunity to translate the error.
99 raise errors.NotABundle(str(e))
100
101 if _do_directive:
102 try:
103 return MergeDirective.from_lines(bytef), transport
104 except errors.NotAMergeDirective:
105 bytef.seek(0)
106
107 return _serializer.read_bundle(bytef), transport
0108
=== modified file 'breezy/tests/__init__.py'
--- breezy/tests/__init__.py 2019-03-12 05:18:19 +0000
+++ breezy/tests/__init__.py 2019-05-29 03:29:58 +0000
@@ -4141,6 +4141,7 @@
4141 'breezy.tests.test_memorytree',4141 'breezy.tests.test_memorytree',
4142 'breezy.tests.test_merge',4142 'breezy.tests.test_merge',
4143 'breezy.tests.test_merge3',4143 'breezy.tests.test_merge3',
4144 'breezy.tests.test_mergeable',
4144 'breezy.tests.test_merge_core',4145 'breezy.tests.test_merge_core',
4145 'breezy.tests.test_merge_directive',4146 'breezy.tests.test_merge_directive',
4146 'breezy.tests.test_mergetools',4147 'breezy.tests.test_mergetools',
41474148
=== modified file 'breezy/tests/test_bundle.py'
--- breezy/tests/test_bundle.py 2019-02-09 02:59:15 +0000
+++ breezy/tests/test_bundle.py 2019-05-29 03:29:58 +0000
@@ -17,10 +17,6 @@
17import bz217import bz2
18from io import BytesIO18from io import BytesIO
19import os19import os
20try:
21 import socketserver
22except ImportError:
23 import SocketServer as socketserver
24import sys20import sys
2521
26from .. import (22from .. import (
@@ -36,10 +32,8 @@
36 bzrdir,32 bzrdir,
37 inventory,33 inventory,
38 )34 )
39from ..bundle import read_mergeable_from_url
40from ..bundle.apply_bundle import install_bundle, merge_bundle35from ..bundle.apply_bundle import install_bundle, merge_bundle
41from ..bundle.bundle_data import BundleTree36from ..bundle.bundle_data import BundleTree
42from ..directory_service import directories
43from ..bundle.serializer import write_bundle, read_bundle, v09, v437from ..bundle.serializer import write_bundle, read_bundle, v09, v4
44from ..bundle.serializer.v08 import BundleSerializerV0838from ..bundle.serializer.v08 import BundleSerializerV08
45from ..bundle.serializer.v09 import BundleSerializerV0939from ..bundle.serializer.v09 import BundleSerializerV09
@@ -48,8 +42,6 @@
48from . import (42from . import (
49 features,43 features,
50 test_commit,44 test_commit,
51 test_read_bundle,
52 test_server,
53 )45 )
54from ..transform import TreeTransform46from ..transform import TreeTransform
5547
@@ -1836,63 +1828,3 @@
1836 self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},1828 self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1837 'info', None, None), record)1829 'info', None, None), record)
1838 self.assertRaises(errors.BadBundle, next, record_iter)1830 self.assertRaises(errors.BadBundle, next, record_iter)
1839
1840
1841class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
1842
1843 def test_read_mergeable_skips_local(self):
1844 """A local bundle named like the URL should not be read.
1845 """
1846 out, wt = test_read_bundle.create_bundle_file(self)
1847
1848 class FooService(object):
1849 """A directory service that always returns source"""
1850
1851 def look_up(self, name, url, purpose=None):
1852 return 'source'
1853 directories.register('foo:', FooService, 'Testing directory service')
1854 self.addCleanup(directories.remove, 'foo:')
1855 self.build_tree_contents([('./foo:bar', out.getvalue())])
1856 self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1857 'foo:bar')
1858
1859 def test_infinite_redirects_are_not_a_bundle(self):
1860 """If a URL causes TooManyRedirections then NotABundle is raised.
1861 """
1862 from .blackbox.test_push import RedirectingMemoryServer
1863 server = RedirectingMemoryServer()
1864 self.start_server(server)
1865 url = server.get_url() + 'infinite-loop'
1866 self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1867
1868 def test_smart_server_connection_reset(self):
1869 """If a smart server connection fails during the attempt to read a
1870 bundle, then the ConnectionReset error should be propagated.
1871 """
1872 # Instantiate a server that will provoke a ConnectionReset
1873 sock_server = DisconnectingServer()
1874 self.start_server(sock_server)
1875 # We don't really care what the url is since the server will close the
1876 # connection without interpreting it
1877 url = sock_server.get_url()
1878 self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
1879
1880
1881class DisconnectingHandler(socketserver.BaseRequestHandler):
1882 """A request handler that immediately closes any connection made to it."""
1883
1884 def handle(self):
1885 self.request.close()
1886
1887
1888class DisconnectingServer(test_server.TestingTCPServerInAThread):
1889
1890 def __init__(self):
1891 super(DisconnectingServer, self).__init__(
1892 ('127.0.0.1', 0),
1893 test_server.TestingTCPServer,
1894 DisconnectingHandler)
1895
1896 def get_url(self):
1897 """Return the url of the server"""
1898 return "bzr://%s:%d/" % self.server.server_address
18991831
=== added file 'breezy/tests/test_mergeable.py'
--- breezy/tests/test_mergeable.py 1970-01-01 00:00:00 +0000
+++ breezy/tests/test_mergeable.py 2019-05-29 03:29:58 +0000
@@ -0,0 +1,91 @@
1# Copyright (C) 2005-2013, 2016 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17try:
18 import socketserver
19except ImportError:
20 import SocketServer as socketserver
21
22from ..mergeable import read_mergeable_from_url
23from ..directory_service import directories
24from .. import (
25 errors,
26 tests,
27 )
28from . import (
29 test_read_bundle,
30 test_server,
31 )
32
33
34class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
35
36 def test_read_mergeable_skips_local(self):
37 """A local bundle named like the URL should not be read.
38 """
39 out, wt = test_read_bundle.create_bundle_file(self)
40
41 class FooService(object):
42 """A directory service that always returns source"""
43
44 def look_up(self, name, url):
45 return 'source'
46 directories.register('foo:', FooService, 'Testing directory service')
47 self.addCleanup(directories.remove, 'foo:')
48 self.build_tree_contents([('./foo:bar', out.getvalue())])
49 self.assertRaises(errors.NotABundle, read_mergeable_from_url,
50 'foo:bar')
51
52 def test_infinite_redirects_are_not_a_bundle(self):
53 """If a URL causes TooManyRedirections then NotABundle is raised.
54 """
55 from .blackbox.test_push import RedirectingMemoryServer
56 server = RedirectingMemoryServer()
57 self.start_server(server)
58 url = server.get_url() + 'infinite-loop'
59 self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
60
61 def test_smart_server_connection_reset(self):
62 """If a smart server connection fails during the attempt to read a
63 bundle, then the ConnectionReset error should be propagated.
64 """
65 # Instantiate a server that will provoke a ConnectionReset
66 sock_server = DisconnectingServer()
67 self.start_server(sock_server)
68 # We don't really care what the url is since the server will close the
69 # connection without interpreting it
70 url = sock_server.get_url()
71 self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
72
73
74class DisconnectingHandler(socketserver.BaseRequestHandler):
75 """A request handler that immediately closes any connection made to it."""
76
77 def handle(self):
78 self.request.close()
79
80
81class DisconnectingServer(test_server.TestingTCPServerInAThread):
82
83 def __init__(self):
84 super(DisconnectingServer, self).__init__(
85 ('127.0.0.1', 0),
86 test_server.TestingTCPServer,
87 DisconnectingHandler)
88
89 def get_url(self):
90 """Return the url of the server"""
91 return "bzr://%s:%d/" % self.server.server_address
092
=== modified file 'breezy/tests/test_read_bundle.py'
--- breezy/tests/test_read_bundle.py 2018-11-12 01:41:38 +0000
+++ breezy/tests/test_read_bundle.py 2019-05-29 03:29:58 +0000
@@ -16,7 +16,7 @@
1616
17"""Test read_bundle works properly across various transports."""17"""Test read_bundle works properly across various transports."""
1818
19import breezy.bundle19import breezy.mergeable
20from ..bundle.serializer import write_bundle20from ..bundle.serializer import write_bundle
21import breezy.bzr.bzrdir21import breezy.bzr.bzrdir
22from .. import errors22from .. import errors
@@ -71,7 +71,7 @@
71 self.create_test_bundle()71 self.create_test_bundle()
7272
73 def read_mergeable_from_url(self, url):73 def read_mergeable_from_url(self, url):
74 return breezy.bundle.read_mergeable_from_url(74 return breezy.mergeable.read_mergeable_from_url(
75 url, possible_transports=self.possible_transports)75 url, possible_transports=self.possible_transports)
7676
77 def get_url(self, relpath=''):77 def get_url(self, relpath=''):

Subscribers

People subscribed via source and target branches