Merge lp:~jelmer/brz/memorytransport-links into lp:brz

Proposed by Jelmer Vernooij on 2018-09-16
Status: Merged
Approved by: Jelmer Vernooij on 2018-09-21
Approved revision: 7123
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/memorytransport-links
Merge into: lp:brz
Diff against target: 174 lines (+55/-9)
4 files modified
breezy/tests/per_transport.py (+10/-0)
breezy/tests/stub_sftp.py (+9/-1)
breezy/transport/local.py (+4/-1)
breezy/transport/memory.py (+32/-7)
To merge this branch: bzr merge lp:~jelmer/brz/memorytransport-links
Reviewer Review Type Date Requested Status
Martin Packman 2018-09-16 Approve on 2018-09-21
Review via email: mp+355006@code.launchpad.net

Commit message

Support symlinks in MemoryTransport, and add tests for Transport.readlink.

Description of the change

Support symlinks in MemoryTransport, and add tests for Transport.readlink.

To post a comment you must log in.
Martin Packman (gz) wrote :

All looks sensible to me, see one inline note about assertion.

review: Approve
lp:~jelmer/brz/memorytransport-links updated on 2018-09-21
7123. By Jelmer Vernooij on 2018-09-21

Add error message.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/tests/per_transport.py'
2--- breezy/tests/per_transport.py 2018-08-04 18:44:34 +0000
3+++ breezy/tests/per_transport.py 2018-09-21 22:47:54 +0000
4@@ -1021,6 +1021,16 @@
5 raise TestSkipped("Transport %s does not support symlinks." %
6 self._server.__class__)
7
8+ self.assertEqual(source_name, t.readlink(link_name))
9+
10+ def test_readlink_nonexistent(self):
11+ t = self.get_transport()
12+ try:
13+ self.assertRaises(NoSuchFile, t.readlink, 'nonexistent')
14+ except TransportNotPossible:
15+ raise TestSkipped("Transport %s does not support symlinks." %
16+ self._server.__class__)
17+
18 def test_list_dir(self):
19 # TODO: Test list_dir, just try once, and if it throws, stop testing
20 t = self.get_transport()
21
22=== modified file 'breezy/tests/stub_sftp.py'
23--- breezy/tests/stub_sftp.py 2018-08-04 18:44:34 +0000
24+++ breezy/tests/stub_sftp.py 2018-09-21 22:47:54 +0000
25@@ -224,6 +224,14 @@
26 return paramiko.SFTPServer.convert_errno(e.errno)
27 return paramiko.SFTP_OK
28
29+ def readlink(self, path):
30+ path = self._realpath(path)
31+ try:
32+ target_path = os.readlink(path)
33+ except OSError as e:
34+ return paramiko.SFTPServer.convert_errno(e.errno)
35+ return target_path
36+
37 def mkdir(self, path, attr):
38 path = self._realpath(path)
39 try:
40@@ -248,7 +256,7 @@
41 return paramiko.SFTPServer.convert_errno(e.errno)
42 return paramiko.SFTP_OK
43
44- # removed: chattr, symlink, readlink
45+ # removed: chattr
46 # (nothing in bzr's sftp transport uses those)
47
48
49
50=== modified file 'breezy/transport/local.py'
51--- breezy/transport/local.py 2017-11-19 18:55:26 +0000
52+++ breezy/transport/local.py 2018-09-21 22:47:54 +0000
53@@ -519,7 +519,10 @@
54 if osutils.host_os_dereferences_symlinks():
55 def readlink(self, relpath):
56 """See Transport.readlink."""
57- return osutils.readlink(self._abspath(relpath))
58+ try:
59+ return osutils.readlink(self._abspath(relpath))
60+ except (IOError, OSError) as e:
61+ self._translate_error(e, relpath)
62
63 if osutils.hardlinks_good():
64 def hardlink(self, source, link_name):
65
66=== modified file 'breezy/transport/memory.py'
67--- breezy/transport/memory.py 2018-06-30 17:27:13 +0000
68+++ breezy/transport/memory.py 2018-09-21 22:47:54 +0000
69@@ -28,7 +28,7 @@
70 )
71 import os
72 import errno
73-from stat import S_IFREG, S_IFDIR
74+from stat import S_IFREG, S_IFDIR, S_IFLNK
75
76 from .. import (
77 transport,
78@@ -39,6 +39,7 @@
79 LockError,
80 InProcessTransport,
81 NoSuchFile,
82+ TransportNotPossible,
83 )
84 from ..transport import (
85 AppendBasedFileStream,
86@@ -50,16 +51,20 @@
87
88 class MemoryStat(object):
89
90- def __init__(self, size, is_dir, perms):
91+ def __init__(self, size, kind, perms):
92 self.st_size = size
93- if not is_dir:
94+ if kind == 'file':
95 if perms is None:
96 perms = 0o644
97 self.st_mode = S_IFREG | perms
98- else:
99+ elif kind == 'directory':
100 if perms is None:
101 perms = 0o755
102 self.st_mode = S_IFDIR | perms
103+ elif kind == 'symlink':
104+ self.st_mode = S_IFLNK | 0o644
105+ else:
106+ raise AssertionError('unknown kind %r' % kind)
107
108
109 class MemoryTransport(transport.Transport):
110@@ -77,6 +82,7 @@
111 self._cwd = url[split:]
112 # dictionaries from absolute path to file mode
113 self._dirs = {'/':None}
114+ self._symlinks = {}
115 self._files = {}
116 self._locks = {}
117
118@@ -88,6 +94,7 @@
119 url = self._scheme + path
120 result = self.__class__(url)
121 result._dirs = self._dirs
122+ result._symlinks = self._symlinks
123 result._files = self._files
124 result._locks = self._locks
125 return result
126@@ -122,7 +129,9 @@
127 def has(self, relpath):
128 """See Transport.has()."""
129 _abspath = self._abspath(relpath)
130- return (_abspath in self._files) or (_abspath in self._dirs)
131+ return ((_abspath in self._files) or
132+ (_abspath in self._dirs) or
133+ (_abspath in self._symlinks))
134
135 def delete(self, relpath):
136 """See Transport.delete()."""
137@@ -254,10 +263,12 @@
138 """See Transport.stat()."""
139 _abspath = self._abspath(relpath)
140 if _abspath in self._files:
141- return MemoryStat(len(self._files[_abspath][0]), False,
142+ return MemoryStat(len(self._files[_abspath][0]), 'file',
143 self._files[_abspath][1])
144 elif _abspath in self._dirs:
145- return MemoryStat(0, True, self._dirs[_abspath])
146+ return MemoryStat(0, 'directory', self._dirs[_abspath])
147+ elif _abspath in self._symlinks:
148+ return MemoryStat(0, 'symlink', 0)
149 else:
150 raise NoSuchFile(_abspath)
151
152@@ -287,8 +298,22 @@
153 pass
154 else:
155 r.append(i)
156+ r = self._symlinks.get('/'.join(r), r)
157 return '/' + '/'.join(r)
158
159+ def symlink(self, source, link_name):
160+ """Create a symlink pointing to source named link_name."""
161+ _abspath = self._abspath(link_name)
162+ self._check_parent(_abspath)
163+ self._symlinks[_abspath] = source.split('/')
164+
165+ def readlink(self, link_name):
166+ _abspath = self._abspath(link_name)
167+ try:
168+ return '/'.join(self._symlinks[_abspath])
169+ except KeyError:
170+ raise NoSuchFile(link_name)
171+
172
173 class _MemoryLock(object):
174 """This makes a lock."""

Subscribers

People subscribed via source and target branches