Mercurial > public > mercurial-scm > hg
comparison mercurial/wireprotov1server.py @ 43077:687b865b95ad
formatting: byteify all mercurial/ and hgext/ string literals
Done with
python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py')
black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**')
# skip-blame mass-reformatting only
Differential Revision: https://phab.mercurial-scm.org/D6972
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:48:39 -0400 |
parents | 2372284d9457 |
children | c59eb1560c44 |
comparison
equal
deleted
inserted
replaced
43076:2372284d9457 | 43077:687b865b95ad |
---|---|
36 ) | 36 ) |
37 | 37 |
38 urlerr = util.urlerr | 38 urlerr = util.urlerr |
39 urlreq = util.urlreq | 39 urlreq = util.urlreq |
40 | 40 |
41 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required') | 41 bundle2requiredmain = _(b'incompatible Mercurial client; bundle2 required') |
42 bundle2requiredhint = _( | 42 bundle2requiredhint = _( |
43 'see https://www.mercurial-scm.org/wiki/' 'IncompatibleClient' | 43 b'see https://www.mercurial-scm.org/wiki/' b'IncompatibleClient' |
44 ) | 44 ) |
45 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint) | 45 bundle2required = b'%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint) |
46 | 46 |
47 | 47 |
48 def clientcompressionsupport(proto): | 48 def clientcompressionsupport(proto): |
49 """Returns a list of compression methods supported by the client. | 49 """Returns a list of compression methods supported by the client. |
50 | 50 |
51 Returns a list of the compression methods supported by the client | 51 Returns a list of the compression methods supported by the client |
52 according to the protocol capabilities. If no such capability has | 52 according to the protocol capabilities. If no such capability has |
53 been announced, fallback to the default of zlib and uncompressed. | 53 been announced, fallback to the default of zlib and uncompressed. |
54 """ | 54 """ |
55 for cap in proto.getprotocaps(): | 55 for cap in proto.getprotocaps(): |
56 if cap.startswith('comp='): | 56 if cap.startswith(b'comp='): |
57 return cap[5:].split(',') | 57 return cap[5:].split(b',') |
58 return ['zlib', 'none'] | 58 return [b'zlib', b'none'] |
59 | 59 |
60 | 60 |
61 # wire protocol command can either return a string or one of these classes. | 61 # wire protocol command can either return a string or one of these classes. |
62 | 62 |
63 | 63 |
66 | 66 |
67 The intent of this function is to serve as a monkeypatch point for | 67 The intent of this function is to serve as a monkeypatch point for |
68 extensions that need commands to operate on different repo views under | 68 extensions that need commands to operate on different repo views under |
69 specialized circumstances. | 69 specialized circumstances. |
70 """ | 70 """ |
71 viewconfig = repo.ui.config('server', 'view') | 71 viewconfig = repo.ui.config(b'server', b'view') |
72 return repo.filtered(viewconfig) | 72 return repo.filtered(viewconfig) |
73 | 73 |
74 | 74 |
75 def dispatch(repo, proto, command): | 75 def dispatch(repo, proto, command): |
76 repo = getdispatchrepo(repo, proto, command) | 76 repo = getdispatchrepo(repo, proto, command) |
87 if k in others: | 87 if k in others: |
88 opts[k] = others[k] | 88 opts[k] = others[k] |
89 del others[k] | 89 del others[k] |
90 if others: | 90 if others: |
91 procutil.stderr.write( | 91 procutil.stderr.write( |
92 "warning: %s ignored unexpected arguments %s\n" | 92 b"warning: %s ignored unexpected arguments %s\n" |
93 % (cmd, ",".join(others)) | 93 % (cmd, b",".join(others)) |
94 ) | 94 ) |
95 return opts | 95 return opts |
96 | 96 |
97 | 97 |
98 def bundle1allowed(repo, action): | 98 def bundle1allowed(repo, action): |
104 2. server.bundle1.<action> | 104 2. server.bundle1.<action> |
105 3. server.bundle1gd (if generaldelta active) | 105 3. server.bundle1gd (if generaldelta active) |
106 4. server.bundle1 | 106 4. server.bundle1 |
107 """ | 107 """ |
108 ui = repo.ui | 108 ui = repo.ui |
109 gd = 'generaldelta' in repo.requirements | 109 gd = b'generaldelta' in repo.requirements |
110 | 110 |
111 if gd: | 111 if gd: |
112 v = ui.configbool('server', 'bundle1gd.%s' % action) | 112 v = ui.configbool(b'server', b'bundle1gd.%s' % action) |
113 if v is not None: | 113 if v is not None: |
114 return v | 114 return v |
115 | 115 |
116 v = ui.configbool('server', 'bundle1.%s' % action) | 116 v = ui.configbool(b'server', b'bundle1.%s' % action) |
117 if v is not None: | 117 if v is not None: |
118 return v | 118 return v |
119 | 119 |
120 if gd: | 120 if gd: |
121 v = ui.configbool('server', 'bundle1gd') | 121 v = ui.configbool(b'server', b'bundle1gd') |
122 if v is not None: | 122 if v is not None: |
123 return v | 123 return v |
124 | 124 |
125 return ui.configbool('server', 'bundle1') | 125 return ui.configbool(b'server', b'bundle1') |
126 | 126 |
127 | 127 |
128 commands = wireprototypes.commanddict() | 128 commands = wireprototypes.commanddict() |
129 | 129 |
130 | 130 |
131 def wireprotocommand(name, args=None, permission='push'): | 131 def wireprotocommand(name, args=None, permission=b'push'): |
132 """Decorator to declare a wire protocol command. | 132 """Decorator to declare a wire protocol command. |
133 | 133 |
134 ``name`` is the name of the wire protocol command being provided. | 134 ``name`` is the name of the wire protocol command being provided. |
135 | 135 |
136 ``args`` defines the named arguments accepted by the command. It is | 136 ``args`` defines the named arguments accepted by the command. It is |
142 respectively. Default is to assume command requires ``push`` permissions | 142 respectively. Default is to assume command requires ``push`` permissions |
143 because otherwise commands not declaring their permissions could modify | 143 because otherwise commands not declaring their permissions could modify |
144 a repository that is supposed to be read-only. | 144 a repository that is supposed to be read-only. |
145 """ | 145 """ |
146 transports = { | 146 transports = { |
147 k for k, v in wireprototypes.TRANSPORTS.items() if v['version'] == 1 | 147 k for k, v in wireprototypes.TRANSPORTS.items() if v[b'version'] == 1 |
148 } | 148 } |
149 | 149 |
150 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to | 150 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to |
151 # SSHv2. | 151 # SSHv2. |
152 # TODO undo this hack when SSH is using the unified frame protocol. | 152 # TODO undo this hack when SSH is using the unified frame protocol. |
153 if name == b'batch': | 153 if name == b'batch': |
154 transports.add(wireprototypes.SSHV2) | 154 transports.add(wireprototypes.SSHV2) |
155 | 155 |
156 if permission not in ('push', 'pull'): | 156 if permission not in (b'push', b'pull'): |
157 raise error.ProgrammingError( | 157 raise error.ProgrammingError( |
158 'invalid wire protocol permission; ' | 158 b'invalid wire protocol permission; ' |
159 'got %s; expected "push" or "pull"' % permission | 159 b'got %s; expected "push" or "pull"' % permission |
160 ) | 160 ) |
161 | 161 |
162 if args is None: | 162 if args is None: |
163 args = '' | 163 args = b'' |
164 | 164 |
165 if not isinstance(args, bytes): | 165 if not isinstance(args, bytes): |
166 raise error.ProgrammingError( | 166 raise error.ProgrammingError( |
167 'arguments for version 1 commands ' 'must be declared as bytes' | 167 b'arguments for version 1 commands ' b'must be declared as bytes' |
168 ) | 168 ) |
169 | 169 |
170 def register(func): | 170 def register(func): |
171 if name in commands: | 171 if name in commands: |
172 raise error.ProgrammingError( | 172 raise error.ProgrammingError( |
173 '%s command already registered ' 'for version 1' % name | 173 b'%s command already registered ' b'for version 1' % name |
174 ) | 174 ) |
175 commands[name] = wireprototypes.commandentry( | 175 commands[name] = wireprototypes.commandentry( |
176 func, args=args, transports=transports, permission=permission | 176 func, args=args, transports=transports, permission=permission |
177 ) | 177 ) |
178 | 178 |
180 | 180 |
181 return register | 181 return register |
182 | 182 |
183 | 183 |
184 # TODO define a more appropriate permissions type to use for this. | 184 # TODO define a more appropriate permissions type to use for this. |
185 @wireprotocommand('batch', 'cmds *', permission='pull') | 185 @wireprotocommand(b'batch', b'cmds *', permission=b'pull') |
186 def batch(repo, proto, cmds, others): | 186 def batch(repo, proto, cmds, others): |
187 unescapearg = wireprototypes.unescapebatcharg | 187 unescapearg = wireprototypes.unescapebatcharg |
188 res = [] | 188 res = [] |
189 for pair in cmds.split(';'): | 189 for pair in cmds.split(b';'): |
190 op, args = pair.split(' ', 1) | 190 op, args = pair.split(b' ', 1) |
191 vals = {} | 191 vals = {} |
192 for a in args.split(','): | 192 for a in args.split(b','): |
193 if a: | 193 if a: |
194 n, v = a.split('=') | 194 n, v = a.split(b'=') |
195 vals[unescapearg(n)] = unescapearg(v) | 195 vals[unescapearg(n)] = unescapearg(v) |
196 func, spec = commands[op] | 196 func, spec = commands[op] |
197 | 197 |
198 # Validate that client has permissions to perform this command. | 198 # Validate that client has permissions to perform this command. |
199 perm = commands[op].permission | 199 perm = commands[op].permission |
200 assert perm in ('push', 'pull') | 200 assert perm in (b'push', b'pull') |
201 proto.checkperm(perm) | 201 proto.checkperm(perm) |
202 | 202 |
203 if spec: | 203 if spec: |
204 keys = spec.split() | 204 keys = spec.split() |
205 data = {} | 205 data = {} |
206 for k in keys: | 206 for k in keys: |
207 if k == '*': | 207 if k == b'*': |
208 star = {} | 208 star = {} |
209 for key in vals.keys(): | 209 for key in vals.keys(): |
210 if key not in keys: | 210 if key not in keys: |
211 star[key] = vals[key] | 211 star[key] = vals[key] |
212 data['*'] = star | 212 data[b'*'] = star |
213 else: | 213 else: |
214 data[k] = vals[k] | 214 data[k] = vals[k] |
215 result = func(repo, proto, *[data[k] for k in keys]) | 215 result = func(repo, proto, *[data[k] for k in keys]) |
216 else: | 216 else: |
217 result = func(repo, proto) | 217 result = func(repo, proto) |
223 assert isinstance(result, (wireprototypes.bytesresponse, bytes)) | 223 assert isinstance(result, (wireprototypes.bytesresponse, bytes)) |
224 if isinstance(result, wireprototypes.bytesresponse): | 224 if isinstance(result, wireprototypes.bytesresponse): |
225 result = result.data | 225 result = result.data |
226 res.append(wireprototypes.escapebatcharg(result)) | 226 res.append(wireprototypes.escapebatcharg(result)) |
227 | 227 |
228 return wireprototypes.bytesresponse(';'.join(res)) | 228 return wireprototypes.bytesresponse(b';'.join(res)) |
229 | 229 |
230 | 230 |
231 @wireprotocommand('between', 'pairs', permission='pull') | 231 @wireprotocommand(b'between', b'pairs', permission=b'pull') |
232 def between(repo, proto, pairs): | 232 def between(repo, proto, pairs): |
233 pairs = [wireprototypes.decodelist(p, '-') for p in pairs.split(" ")] | 233 pairs = [wireprototypes.decodelist(p, b'-') for p in pairs.split(b" ")] |
234 r = [] | 234 r = [] |
235 for b in repo.between(pairs): | 235 for b in repo.between(pairs): |
236 r.append(wireprototypes.encodelist(b) + "\n") | 236 r.append(wireprototypes.encodelist(b) + b"\n") |
237 | 237 |
238 return wireprototypes.bytesresponse(''.join(r)) | 238 return wireprototypes.bytesresponse(b''.join(r)) |
239 | 239 |
240 | 240 |
241 @wireprotocommand('branchmap', permission='pull') | 241 @wireprotocommand(b'branchmap', permission=b'pull') |
242 def branchmap(repo, proto): | 242 def branchmap(repo, proto): |
243 branchmap = repo.branchmap() | 243 branchmap = repo.branchmap() |
244 heads = [] | 244 heads = [] |
245 for branch, nodes in branchmap.iteritems(): | 245 for branch, nodes in branchmap.iteritems(): |
246 branchname = urlreq.quote(encoding.fromlocal(branch)) | 246 branchname = urlreq.quote(encoding.fromlocal(branch)) |
247 branchnodes = wireprototypes.encodelist(nodes) | 247 branchnodes = wireprototypes.encodelist(nodes) |
248 heads.append('%s %s' % (branchname, branchnodes)) | 248 heads.append(b'%s %s' % (branchname, branchnodes)) |
249 | 249 |
250 return wireprototypes.bytesresponse('\n'.join(heads)) | 250 return wireprototypes.bytesresponse(b'\n'.join(heads)) |
251 | 251 |
252 | 252 |
253 @wireprotocommand('branches', 'nodes', permission='pull') | 253 @wireprotocommand(b'branches', b'nodes', permission=b'pull') |
254 def branches(repo, proto, nodes): | 254 def branches(repo, proto, nodes): |
255 nodes = wireprototypes.decodelist(nodes) | 255 nodes = wireprototypes.decodelist(nodes) |
256 r = [] | 256 r = [] |
257 for b in repo.branches(nodes): | 257 for b in repo.branches(nodes): |
258 r.append(wireprototypes.encodelist(b) + "\n") | 258 r.append(wireprototypes.encodelist(b) + b"\n") |
259 | 259 |
260 return wireprototypes.bytesresponse(''.join(r)) | 260 return wireprototypes.bytesresponse(b''.join(r)) |
261 | 261 |
262 | 262 |
263 @wireprotocommand('clonebundles', '', permission='pull') | 263 @wireprotocommand(b'clonebundles', b'', permission=b'pull') |
264 def clonebundles(repo, proto): | 264 def clonebundles(repo, proto): |
265 """Server command for returning info for available bundles to seed clones. | 265 """Server command for returning info for available bundles to seed clones. |
266 | 266 |
267 Clients will parse this response and determine what bundle to fetch. | 267 Clients will parse this response and determine what bundle to fetch. |
268 | 268 |
269 Extensions may wrap this command to filter or dynamically emit data | 269 Extensions may wrap this command to filter or dynamically emit data |
270 depending on the request. e.g. you could advertise URLs for the closest | 270 depending on the request. e.g. you could advertise URLs for the closest |
271 data center given the client's IP address. | 271 data center given the client's IP address. |
272 """ | 272 """ |
273 return wireprototypes.bytesresponse( | 273 return wireprototypes.bytesresponse( |
274 repo.vfs.tryread('clonebundles.manifest') | 274 repo.vfs.tryread(b'clonebundles.manifest') |
275 ) | 275 ) |
276 | 276 |
277 | 277 |
278 wireprotocaps = [ | 278 wireprotocaps = [ |
279 'lookup', | 279 b'lookup', |
280 'branchmap', | 280 b'branchmap', |
281 'pushkey', | 281 b'pushkey', |
282 'known', | 282 b'known', |
283 'getbundle', | 283 b'getbundle', |
284 'unbundlehash', | 284 b'unbundlehash', |
285 ] | 285 ] |
286 | 286 |
287 | 287 |
288 def _capabilities(repo, proto): | 288 def _capabilities(repo, proto): |
289 """return a list of capabilities for a repo | 289 """return a list of capabilities for a repo |
298 # copy to prevent modification of the global list | 298 # copy to prevent modification of the global list |
299 caps = list(wireprotocaps) | 299 caps = list(wireprotocaps) |
300 | 300 |
301 # Command of same name as capability isn't exposed to version 1 of | 301 # Command of same name as capability isn't exposed to version 1 of |
302 # transports. So conditionally add it. | 302 # transports. So conditionally add it. |
303 if commands.commandavailable('changegroupsubset', proto): | 303 if commands.commandavailable(b'changegroupsubset', proto): |
304 caps.append('changegroupsubset') | 304 caps.append(b'changegroupsubset') |
305 | 305 |
306 if streamclone.allowservergeneration(repo): | 306 if streamclone.allowservergeneration(repo): |
307 if repo.ui.configbool('server', 'preferuncompressed'): | 307 if repo.ui.configbool(b'server', b'preferuncompressed'): |
308 caps.append('stream-preferred') | 308 caps.append(b'stream-preferred') |
309 requiredformats = repo.requirements & repo.supportedformats | 309 requiredformats = repo.requirements & repo.supportedformats |
310 # if our local revlogs are just revlogv1, add 'stream' cap | 310 # if our local revlogs are just revlogv1, add 'stream' cap |
311 if not requiredformats - {'revlogv1'}: | 311 if not requiredformats - {b'revlogv1'}: |
312 caps.append('stream') | 312 caps.append(b'stream') |
313 # otherwise, add 'streamreqs' detailing our local revlog format | 313 # otherwise, add 'streamreqs' detailing our local revlog format |
314 else: | 314 else: |
315 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats))) | 315 caps.append(b'streamreqs=%s' % b','.join(sorted(requiredformats))) |
316 if repo.ui.configbool('experimental', 'bundle2-advertise'): | 316 if repo.ui.configbool(b'experimental', b'bundle2-advertise'): |
317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server')) | 317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=b'server')) |
318 caps.append('bundle2=' + urlreq.quote(capsblob)) | 318 caps.append(b'bundle2=' + urlreq.quote(capsblob)) |
319 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority)) | 319 caps.append(b'unbundle=%s' % b','.join(bundle2.bundlepriority)) |
320 | 320 |
321 if repo.ui.configbool('experimental', 'narrow'): | 321 if repo.ui.configbool(b'experimental', b'narrow'): |
322 caps.append(wireprototypes.NARROWCAP) | 322 caps.append(wireprototypes.NARROWCAP) |
323 if repo.ui.configbool('experimental', 'narrowservebrokenellipses'): | 323 if repo.ui.configbool(b'experimental', b'narrowservebrokenellipses'): |
324 caps.append(wireprototypes.ELLIPSESCAP) | 324 caps.append(wireprototypes.ELLIPSESCAP) |
325 | 325 |
326 return proto.addcapabilities(repo, caps) | 326 return proto.addcapabilities(repo, caps) |
327 | 327 |
328 | 328 |
329 # If you are writing an extension and consider wrapping this function. Wrap | 329 # If you are writing an extension and consider wrapping this function. Wrap |
330 # `_capabilities` instead. | 330 # `_capabilities` instead. |
331 @wireprotocommand('capabilities', permission='pull') | 331 @wireprotocommand(b'capabilities', permission=b'pull') |
332 def capabilities(repo, proto): | 332 def capabilities(repo, proto): |
333 caps = _capabilities(repo, proto) | 333 caps = _capabilities(repo, proto) |
334 return wireprototypes.bytesresponse(' '.join(sorted(caps))) | 334 return wireprototypes.bytesresponse(b' '.join(sorted(caps))) |
335 | 335 |
336 | 336 |
337 @wireprotocommand('changegroup', 'roots', permission='pull') | 337 @wireprotocommand(b'changegroup', b'roots', permission=b'pull') |
338 def changegroup(repo, proto, roots): | 338 def changegroup(repo, proto, roots): |
339 nodes = wireprototypes.decodelist(roots) | 339 nodes = wireprototypes.decodelist(roots) |
340 outgoing = discovery.outgoing( | 340 outgoing = discovery.outgoing( |
341 repo, missingroots=nodes, missingheads=repo.heads() | 341 repo, missingroots=nodes, missingheads=repo.heads() |
342 ) | 342 ) |
343 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') | 343 cg = changegroupmod.makechangegroup(repo, outgoing, b'01', b'serve') |
344 gen = iter(lambda: cg.read(32768), '') | 344 gen = iter(lambda: cg.read(32768), b'') |
345 return wireprototypes.streamres(gen=gen) | 345 return wireprototypes.streamres(gen=gen) |
346 | 346 |
347 | 347 |
348 @wireprotocommand('changegroupsubset', 'bases heads', permission='pull') | 348 @wireprotocommand(b'changegroupsubset', b'bases heads', permission=b'pull') |
349 def changegroupsubset(repo, proto, bases, heads): | 349 def changegroupsubset(repo, proto, bases, heads): |
350 bases = wireprototypes.decodelist(bases) | 350 bases = wireprototypes.decodelist(bases) |
351 heads = wireprototypes.decodelist(heads) | 351 heads = wireprototypes.decodelist(heads) |
352 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads) | 352 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads) |
353 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') | 353 cg = changegroupmod.makechangegroup(repo, outgoing, b'01', b'serve') |
354 gen = iter(lambda: cg.read(32768), '') | 354 gen = iter(lambda: cg.read(32768), b'') |
355 return wireprototypes.streamres(gen=gen) | 355 return wireprototypes.streamres(gen=gen) |
356 | 356 |
357 | 357 |
358 @wireprotocommand('debugwireargs', 'one two *', permission='pull') | 358 @wireprotocommand(b'debugwireargs', b'one two *', permission=b'pull') |
359 def debugwireargs(repo, proto, one, two, others): | 359 def debugwireargs(repo, proto, one, two, others): |
360 # only accept optional args from the known set | 360 # only accept optional args from the known set |
361 opts = options('debugwireargs', ['three', 'four'], others) | 361 opts = options(b'debugwireargs', [b'three', b'four'], others) |
362 return wireprototypes.bytesresponse( | 362 return wireprototypes.bytesresponse( |
363 repo.debugwireargs(one, two, **pycompat.strkwargs(opts)) | 363 repo.debugwireargs(one, two, **pycompat.strkwargs(opts)) |
364 ) | 364 ) |
365 | 365 |
366 | 366 |
379 E.g. do not send a bundle of all changes if the client wants only | 379 E.g. do not send a bundle of all changes if the client wants only |
380 one specific branch of many. | 380 one specific branch of many. |
381 """ | 381 """ |
382 | 382 |
383 def decodehexstring(s): | 383 def decodehexstring(s): |
384 return {binascii.unhexlify(h) for h in s.split(';')} | 384 return {binascii.unhexlify(h) for h in s.split(b';')} |
385 | 385 |
386 manifest = repo.vfs.tryread('pullbundles.manifest') | 386 manifest = repo.vfs.tryread(b'pullbundles.manifest') |
387 if not manifest: | 387 if not manifest: |
388 return None | 388 return None |
389 res = exchange.parseclonebundlesmanifest(repo, manifest) | 389 res = exchange.parseclonebundlesmanifest(repo, manifest) |
390 res = exchange.filterclonebundleentries(repo, res) | 390 res = exchange.filterclonebundleentries(repo, res) |
391 if not res: | 391 if not res: |
393 cl = repo.changelog | 393 cl = repo.changelog |
394 heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True) | 394 heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True) |
395 common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True) | 395 common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True) |
396 compformats = clientcompressionsupport(proto) | 396 compformats = clientcompressionsupport(proto) |
397 for entry in res: | 397 for entry in res: |
398 comp = entry.get('COMPRESSION') | 398 comp = entry.get(b'COMPRESSION') |
399 altcomp = util.compengines._bundlenames.get(comp) | 399 altcomp = util.compengines._bundlenames.get(comp) |
400 if comp and comp not in compformats and altcomp not in compformats: | 400 if comp and comp not in compformats and altcomp not in compformats: |
401 continue | 401 continue |
402 # No test yet for VERSION, since V2 is supported by any client | 402 # No test yet for VERSION, since V2 is supported by any client |
403 # that advertises partial pulls | 403 # that advertises partial pulls |
404 if 'heads' in entry: | 404 if b'heads' in entry: |
405 try: | 405 try: |
406 bundle_heads = decodehexstring(entry['heads']) | 406 bundle_heads = decodehexstring(entry[b'heads']) |
407 except TypeError: | 407 except TypeError: |
408 # Bad heads entry | 408 # Bad heads entry |
409 continue | 409 continue |
410 if bundle_heads.issubset(common): | 410 if bundle_heads.issubset(common): |
411 continue # Nothing new | 411 continue # Nothing new |
414 if any( | 414 if any( |
415 cl.rev(rev) not in heads_anc and cl.rev(rev) not in common_anc | 415 cl.rev(rev) not in heads_anc and cl.rev(rev) not in common_anc |
416 for rev in bundle_heads | 416 for rev in bundle_heads |
417 ): | 417 ): |
418 continue | 418 continue |
419 if 'bases' in entry: | 419 if b'bases' in entry: |
420 try: | 420 try: |
421 bundle_bases = decodehexstring(entry['bases']) | 421 bundle_bases = decodehexstring(entry[b'bases']) |
422 except TypeError: | 422 except TypeError: |
423 # Bad bases entry | 423 # Bad bases entry |
424 continue | 424 continue |
425 if not all(cl.rev(rev) in common_anc for rev in bundle_bases): | 425 if not all(cl.rev(rev) in common_anc for rev in bundle_bases): |
426 continue | 426 continue |
427 path = entry['URL'] | 427 path = entry[b'URL'] |
428 repo.ui.debug('sending pullbundle "%s"\n' % path) | 428 repo.ui.debug(b'sending pullbundle "%s"\n' % path) |
429 try: | 429 try: |
430 return repo.vfs.open(path) | 430 return repo.vfs.open(path) |
431 except IOError: | 431 except IOError: |
432 repo.ui.debug('pullbundle "%s" not accessible\n' % path) | 432 repo.ui.debug(b'pullbundle "%s" not accessible\n' % path) |
433 continue | 433 continue |
434 return None | 434 return None |
435 | 435 |
436 | 436 |
437 @wireprotocommand('getbundle', '*', permission='pull') | 437 @wireprotocommand(b'getbundle', b'*', permission=b'pull') |
438 def getbundle(repo, proto, others): | 438 def getbundle(repo, proto, others): |
439 opts = options( | 439 opts = options( |
440 'getbundle', wireprototypes.GETBUNDLE_ARGUMENTS.keys(), others | 440 b'getbundle', wireprototypes.GETBUNDLE_ARGUMENTS.keys(), others |
441 ) | 441 ) |
442 for k, v in opts.iteritems(): | 442 for k, v in opts.iteritems(): |
443 keytype = wireprototypes.GETBUNDLE_ARGUMENTS[k] | 443 keytype = wireprototypes.GETBUNDLE_ARGUMENTS[k] |
444 if keytype == 'nodes': | 444 if keytype == b'nodes': |
445 opts[k] = wireprototypes.decodelist(v) | 445 opts[k] = wireprototypes.decodelist(v) |
446 elif keytype == 'csv': | 446 elif keytype == b'csv': |
447 opts[k] = list(v.split(',')) | 447 opts[k] = list(v.split(b',')) |
448 elif keytype == 'scsv': | 448 elif keytype == b'scsv': |
449 opts[k] = set(v.split(',')) | 449 opts[k] = set(v.split(b',')) |
450 elif keytype == 'boolean': | 450 elif keytype == b'boolean': |
451 # Client should serialize False as '0', which is a non-empty string | 451 # Client should serialize False as '0', which is a non-empty string |
452 # so it evaluates as a True bool. | 452 # so it evaluates as a True bool. |
453 if v == '0': | 453 if v == b'0': |
454 opts[k] = False | 454 opts[k] = False |
455 else: | 455 else: |
456 opts[k] = bool(v) | 456 opts[k] = bool(v) |
457 elif keytype != 'plain': | 457 elif keytype != b'plain': |
458 raise KeyError('unknown getbundle option type %s' % keytype) | 458 raise KeyError(b'unknown getbundle option type %s' % keytype) |
459 | 459 |
460 if not bundle1allowed(repo, 'pull'): | 460 if not bundle1allowed(repo, b'pull'): |
461 if not exchange.bundle2requested(opts.get('bundlecaps')): | 461 if not exchange.bundle2requested(opts.get(b'bundlecaps')): |
462 if proto.name == 'http-v1': | 462 if proto.name == b'http-v1': |
463 return wireprototypes.ooberror(bundle2required) | 463 return wireprototypes.ooberror(bundle2required) |
464 raise error.Abort(bundle2requiredmain, hint=bundle2requiredhint) | 464 raise error.Abort(bundle2requiredmain, hint=bundle2requiredhint) |
465 | 465 |
466 try: | 466 try: |
467 clheads = set(repo.changelog.heads()) | 467 clheads = set(repo.changelog.heads()) |
468 heads = set(opts.get('heads', set())) | 468 heads = set(opts.get(b'heads', set())) |
469 common = set(opts.get('common', set())) | 469 common = set(opts.get(b'common', set())) |
470 common.discard(nullid) | 470 common.discard(nullid) |
471 if ( | 471 if ( |
472 repo.ui.configbool('server', 'pullbundle') | 472 repo.ui.configbool(b'server', b'pullbundle') |
473 and 'partial-pull' in proto.getprotocaps() | 473 and b'partial-pull' in proto.getprotocaps() |
474 ): | 474 ): |
475 # Check if a pre-built bundle covers this request. | 475 # Check if a pre-built bundle covers this request. |
476 bundle = find_pullbundle(repo, proto, opts, clheads, heads, common) | 476 bundle = find_pullbundle(repo, proto, opts, clheads, heads, common) |
477 if bundle: | 477 if bundle: |
478 return wireprototypes.streamres( | 478 return wireprototypes.streamres( |
479 gen=util.filechunkiter(bundle), prefer_uncompressed=True | 479 gen=util.filechunkiter(bundle), prefer_uncompressed=True |
480 ) | 480 ) |
481 | 481 |
482 if repo.ui.configbool('server', 'disablefullbundle'): | 482 if repo.ui.configbool(b'server', b'disablefullbundle'): |
483 # Check to see if this is a full clone. | 483 # Check to see if this is a full clone. |
484 changegroup = opts.get('cg', True) | 484 changegroup = opts.get(b'cg', True) |
485 if changegroup and not common and clheads == heads: | 485 if changegroup and not common and clheads == heads: |
486 raise error.Abort( | 486 raise error.Abort( |
487 _('server has pull-based clones disabled'), | 487 _(b'server has pull-based clones disabled'), |
488 hint=_('remove --pull if specified or upgrade Mercurial'), | 488 hint=_(b'remove --pull if specified or upgrade Mercurial'), |
489 ) | 489 ) |
490 | 490 |
491 info, chunks = exchange.getbundlechunks( | 491 info, chunks = exchange.getbundlechunks( |
492 repo, 'serve', **pycompat.strkwargs(opts) | 492 repo, b'serve', **pycompat.strkwargs(opts) |
493 ) | 493 ) |
494 prefercompressed = info.get('prefercompressed', True) | 494 prefercompressed = info.get(b'prefercompressed', True) |
495 except error.Abort as exc: | 495 except error.Abort as exc: |
496 # cleanly forward Abort error to the client | 496 # cleanly forward Abort error to the client |
497 if not exchange.bundle2requested(opts.get('bundlecaps')): | 497 if not exchange.bundle2requested(opts.get(b'bundlecaps')): |
498 if proto.name == 'http-v1': | 498 if proto.name == b'http-v1': |
499 return wireprototypes.ooberror(pycompat.bytestr(exc) + '\n') | 499 return wireprototypes.ooberror(pycompat.bytestr(exc) + b'\n') |
500 raise # cannot do better for bundle1 + ssh | 500 raise # cannot do better for bundle1 + ssh |
501 # bundle2 request expect a bundle2 reply | 501 # bundle2 request expect a bundle2 reply |
502 bundler = bundle2.bundle20(repo.ui) | 502 bundler = bundle2.bundle20(repo.ui) |
503 manargs = [('message', pycompat.bytestr(exc))] | 503 manargs = [(b'message', pycompat.bytestr(exc))] |
504 advargs = [] | 504 advargs = [] |
505 if exc.hint is not None: | 505 if exc.hint is not None: |
506 advargs.append(('hint', exc.hint)) | 506 advargs.append((b'hint', exc.hint)) |
507 bundler.addpart(bundle2.bundlepart('error:abort', manargs, advargs)) | 507 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs)) |
508 chunks = bundler.getchunks() | 508 chunks = bundler.getchunks() |
509 prefercompressed = False | 509 prefercompressed = False |
510 | 510 |
511 return wireprototypes.streamres( | 511 return wireprototypes.streamres( |
512 gen=chunks, prefer_uncompressed=not prefercompressed | 512 gen=chunks, prefer_uncompressed=not prefercompressed |
513 ) | 513 ) |
514 | 514 |
515 | 515 |
516 @wireprotocommand('heads', permission='pull') | 516 @wireprotocommand(b'heads', permission=b'pull') |
517 def heads(repo, proto): | 517 def heads(repo, proto): |
518 h = repo.heads() | 518 h = repo.heads() |
519 return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + '\n') | 519 return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n') |
520 | 520 |
521 | 521 |
522 @wireprotocommand('hello', permission='pull') | 522 @wireprotocommand(b'hello', permission=b'pull') |
523 def hello(repo, proto): | 523 def hello(repo, proto): |
524 """Called as part of SSH handshake to obtain server info. | 524 """Called as part of SSH handshake to obtain server info. |
525 | 525 |
526 Returns a list of lines describing interesting things about the | 526 Returns a list of lines describing interesting things about the |
527 server, in an RFC822-like format. | 527 server, in an RFC822-like format. |
530 line of space separated tokens describing server abilities: | 530 line of space separated tokens describing server abilities: |
531 | 531 |
532 capabilities: <token0> <token1> <token2> | 532 capabilities: <token0> <token1> <token2> |
533 """ | 533 """ |
534 caps = capabilities(repo, proto).data | 534 caps = capabilities(repo, proto).data |
535 return wireprototypes.bytesresponse('capabilities: %s\n' % caps) | 535 return wireprototypes.bytesresponse(b'capabilities: %s\n' % caps) |
536 | 536 |
537 | 537 |
538 @wireprotocommand('listkeys', 'namespace', permission='pull') | 538 @wireprotocommand(b'listkeys', b'namespace', permission=b'pull') |
539 def listkeys(repo, proto, namespace): | 539 def listkeys(repo, proto, namespace): |
540 d = sorted(repo.listkeys(encoding.tolocal(namespace)).items()) | 540 d = sorted(repo.listkeys(encoding.tolocal(namespace)).items()) |
541 return wireprototypes.bytesresponse(pushkeymod.encodekeys(d)) | 541 return wireprototypes.bytesresponse(pushkeymod.encodekeys(d)) |
542 | 542 |
543 | 543 |
544 @wireprotocommand('lookup', 'key', permission='pull') | 544 @wireprotocommand(b'lookup', b'key', permission=b'pull') |
545 def lookup(repo, proto, key): | 545 def lookup(repo, proto, key): |
546 try: | 546 try: |
547 k = encoding.tolocal(key) | 547 k = encoding.tolocal(key) |
548 n = repo.lookup(k) | 548 n = repo.lookup(k) |
549 r = hex(n) | 549 r = hex(n) |
550 success = 1 | 550 success = 1 |
551 except Exception as inst: | 551 except Exception as inst: |
552 r = stringutil.forcebytestr(inst) | 552 r = stringutil.forcebytestr(inst) |
553 success = 0 | 553 success = 0 |
554 return wireprototypes.bytesresponse('%d %s\n' % (success, r)) | 554 return wireprototypes.bytesresponse(b'%d %s\n' % (success, r)) |
555 | 555 |
556 | 556 |
557 @wireprotocommand('known', 'nodes *', permission='pull') | 557 @wireprotocommand(b'known', b'nodes *', permission=b'pull') |
558 def known(repo, proto, nodes, others): | 558 def known(repo, proto, nodes, others): |
559 v = ''.join( | 559 v = b''.join( |
560 b and '1' or '0' for b in repo.known(wireprototypes.decodelist(nodes)) | 560 b and b'1' or b'0' for b in repo.known(wireprototypes.decodelist(nodes)) |
561 ) | 561 ) |
562 return wireprototypes.bytesresponse(v) | 562 return wireprototypes.bytesresponse(v) |
563 | 563 |
564 | 564 |
565 @wireprotocommand('protocaps', 'caps', permission='pull') | 565 @wireprotocommand(b'protocaps', b'caps', permission=b'pull') |
566 def protocaps(repo, proto, caps): | 566 def protocaps(repo, proto, caps): |
567 if proto.name == wireprototypes.SSHV1: | 567 if proto.name == wireprototypes.SSHV1: |
568 proto._protocaps = set(caps.split(' ')) | 568 proto._protocaps = set(caps.split(b' ')) |
569 return wireprototypes.bytesresponse('OK') | 569 return wireprototypes.bytesresponse(b'OK') |
570 | 570 |
571 | 571 |
572 @wireprotocommand('pushkey', 'namespace key old new', permission='push') | 572 @wireprotocommand(b'pushkey', b'namespace key old new', permission=b'push') |
573 def pushkey(repo, proto, namespace, key, old, new): | 573 def pushkey(repo, proto, namespace, key, old, new): |
574 # compatibility with pre-1.8 clients which were accidentally | 574 # compatibility with pre-1.8 clients which were accidentally |
575 # sending raw binary nodes rather than utf-8-encoded hex | 575 # sending raw binary nodes rather than utf-8-encoded hex |
576 if len(new) == 20 and stringutil.escapestr(new) != new: | 576 if len(new) == 20 and stringutil.escapestr(new) != new: |
577 # looks like it could be a binary node | 577 # looks like it could be a binary node |
592 new, | 592 new, |
593 ) | 593 ) |
594 or False | 594 or False |
595 ) | 595 ) |
596 | 596 |
597 output = output.getvalue() if output else '' | 597 output = output.getvalue() if output else b'' |
598 return wireprototypes.bytesresponse('%d\n%s' % (int(r), output)) | 598 return wireprototypes.bytesresponse(b'%d\n%s' % (int(r), output)) |
599 | 599 |
600 | 600 |
601 @wireprotocommand('stream_out', permission='pull') | 601 @wireprotocommand(b'stream_out', permission=b'pull') |
602 def stream(repo, proto): | 602 def stream(repo, proto): |
603 '''If the server supports streaming clone, it advertises the "stream" | 603 '''If the server supports streaming clone, it advertises the "stream" |
604 capability with a value representing the version and flags of the repo | 604 capability with a value representing the version and flags of the repo |
605 it is serving. Client checks to see if it understands the format. | 605 it is serving. Client checks to see if it understands the format. |
606 ''' | 606 ''' |
607 return wireprototypes.streamreslegacy(streamclone.generatev1wireproto(repo)) | 607 return wireprototypes.streamreslegacy(streamclone.generatev1wireproto(repo)) |
608 | 608 |
609 | 609 |
610 @wireprotocommand('unbundle', 'heads', permission='push') | 610 @wireprotocommand(b'unbundle', b'heads', permission=b'push') |
611 def unbundle(repo, proto, heads): | 611 def unbundle(repo, proto, heads): |
612 their_heads = wireprototypes.decodelist(heads) | 612 their_heads = wireprototypes.decodelist(heads) |
613 | 613 |
614 with proto.mayberedirectstdio() as output: | 614 with proto.mayberedirectstdio() as output: |
615 try: | 615 try: |
616 exchange.check_heads(repo, their_heads, 'preparing changes') | 616 exchange.check_heads(repo, their_heads, b'preparing changes') |
617 cleanup = lambda: None | 617 cleanup = lambda: None |
618 try: | 618 try: |
619 payload = proto.getpayload() | 619 payload = proto.getpayload() |
620 if repo.ui.configbool('server', 'streamunbundle'): | 620 if repo.ui.configbool(b'server', b'streamunbundle'): |
621 | 621 |
622 def cleanup(): | 622 def cleanup(): |
623 # Ensure that the full payload is consumed, so | 623 # Ensure that the full payload is consumed, so |
624 # that the connection doesn't contain trailing garbage. | 624 # that the connection doesn't contain trailing garbage. |
625 for p in payload: | 625 for p in payload: |
634 if fp: | 634 if fp: |
635 fp.close() | 635 fp.close() |
636 if tempname: | 636 if tempname: |
637 os.unlink(tempname) | 637 os.unlink(tempname) |
638 | 638 |
639 fd, tempname = pycompat.mkstemp(prefix='hg-unbundle-') | 639 fd, tempname = pycompat.mkstemp(prefix=b'hg-unbundle-') |
640 repo.ui.debug( | 640 repo.ui.debug( |
641 'redirecting incoming bundle to %s\n' % tempname | 641 b'redirecting incoming bundle to %s\n' % tempname |
642 ) | 642 ) |
643 fp = os.fdopen(fd, pycompat.sysstr('wb+')) | 643 fp = os.fdopen(fd, pycompat.sysstr(b'wb+')) |
644 for p in payload: | 644 for p in payload: |
645 fp.write(p) | 645 fp.write(p) |
646 fp.seek(0) | 646 fp.seek(0) |
647 | 647 |
648 gen = exchange.readbundle(repo.ui, fp, None) | 648 gen = exchange.readbundle(repo.ui, fp, None) |
649 if isinstance( | 649 if isinstance( |
650 gen, changegroupmod.cg1unpacker | 650 gen, changegroupmod.cg1unpacker |
651 ) and not bundle1allowed(repo, 'push'): | 651 ) and not bundle1allowed(repo, b'push'): |
652 if proto.name == 'http-v1': | 652 if proto.name == b'http-v1': |
653 # need to special case http because stderr do not get to | 653 # need to special case http because stderr do not get to |
654 # the http client on failed push so we need to abuse | 654 # the http client on failed push so we need to abuse |
655 # some other error type to make sure the message get to | 655 # some other error type to make sure the message get to |
656 # the user. | 656 # the user. |
657 return wireprototypes.ooberror(bundle2required) | 657 return wireprototypes.ooberror(bundle2required) |
658 raise error.Abort( | 658 raise error.Abort( |
659 bundle2requiredmain, hint=bundle2requiredhint | 659 bundle2requiredmain, hint=bundle2requiredhint |
660 ) | 660 ) |
661 | 661 |
662 r = exchange.unbundle( | 662 r = exchange.unbundle( |
663 repo, gen, their_heads, 'serve', proto.client() | 663 repo, gen, their_heads, b'serve', proto.client() |
664 ) | 664 ) |
665 if util.safehasattr(r, 'addpart'): | 665 if util.safehasattr(r, b'addpart'): |
666 # The return looks streamable, we are in the bundle2 case | 666 # The return looks streamable, we are in the bundle2 case |
667 # and should return a stream. | 667 # and should return a stream. |
668 return wireprototypes.streamreslegacy(gen=r.getchunks()) | 668 return wireprototypes.streamreslegacy(gen=r.getchunks()) |
669 return wireprototypes.pushres( | 669 return wireprototypes.pushres( |
670 r, output.getvalue() if output else '' | 670 r, output.getvalue() if output else b'' |
671 ) | 671 ) |
672 | 672 |
673 finally: | 673 finally: |
674 cleanup() | 674 cleanup() |
675 | 675 |
681 except error.Abort: | 681 except error.Abort: |
682 # The old code we moved used procutil.stderr directly. | 682 # The old code we moved used procutil.stderr directly. |
683 # We did not change it to minimise code change. | 683 # We did not change it to minimise code change. |
684 # This need to be moved to something proper. | 684 # This need to be moved to something proper. |
685 # Feel free to do it. | 685 # Feel free to do it. |
686 procutil.stderr.write("abort: %s\n" % exc) | 686 procutil.stderr.write(b"abort: %s\n" % exc) |
687 if exc.hint is not None: | 687 if exc.hint is not None: |
688 procutil.stderr.write("(%s)\n" % exc.hint) | 688 procutil.stderr.write(b"(%s)\n" % exc.hint) |
689 procutil.stderr.flush() | 689 procutil.stderr.flush() |
690 return wireprototypes.pushres( | 690 return wireprototypes.pushres( |
691 0, output.getvalue() if output else '' | 691 0, output.getvalue() if output else b'' |
692 ) | 692 ) |
693 except error.PushRaced: | 693 except error.PushRaced: |
694 return wireprototypes.pusherr( | 694 return wireprototypes.pusherr( |
695 pycompat.bytestr(exc), | 695 pycompat.bytestr(exc), |
696 output.getvalue() if output else '', | 696 output.getvalue() if output else b'', |
697 ) | 697 ) |
698 | 698 |
699 bundler = bundle2.bundle20(repo.ui) | 699 bundler = bundle2.bundle20(repo.ui) |
700 for out in getattr(exc, '_bundle2salvagedoutput', ()): | 700 for out in getattr(exc, '_bundle2salvagedoutput', ()): |
701 bundler.addpart(out) | 701 bundler.addpart(out) |
705 except error.PushkeyFailed as exc: | 705 except error.PushkeyFailed as exc: |
706 # check client caps | 706 # check client caps |
707 remotecaps = getattr(exc, '_replycaps', None) | 707 remotecaps = getattr(exc, '_replycaps', None) |
708 if ( | 708 if ( |
709 remotecaps is not None | 709 remotecaps is not None |
710 and 'pushkey' not in remotecaps.get('error', ()) | 710 and b'pushkey' not in remotecaps.get(b'error', ()) |
711 ): | 711 ): |
712 # no support remote side, fallback to Abort handler. | 712 # no support remote side, fallback to Abort handler. |
713 raise | 713 raise |
714 part = bundler.newpart('error:pushkey') | 714 part = bundler.newpart(b'error:pushkey') |
715 part.addparam('in-reply-to', exc.partid) | 715 part.addparam(b'in-reply-to', exc.partid) |
716 if exc.namespace is not None: | 716 if exc.namespace is not None: |
717 part.addparam( | 717 part.addparam( |
718 'namespace', exc.namespace, mandatory=False | 718 b'namespace', exc.namespace, mandatory=False |
719 ) | 719 ) |
720 if exc.key is not None: | 720 if exc.key is not None: |
721 part.addparam('key', exc.key, mandatory=False) | 721 part.addparam(b'key', exc.key, mandatory=False) |
722 if exc.new is not None: | 722 if exc.new is not None: |
723 part.addparam('new', exc.new, mandatory=False) | 723 part.addparam(b'new', exc.new, mandatory=False) |
724 if exc.old is not None: | 724 if exc.old is not None: |
725 part.addparam('old', exc.old, mandatory=False) | 725 part.addparam(b'old', exc.old, mandatory=False) |
726 if exc.ret is not None: | 726 if exc.ret is not None: |
727 part.addparam('ret', exc.ret, mandatory=False) | 727 part.addparam(b'ret', exc.ret, mandatory=False) |
728 except error.BundleValueError as exc: | 728 except error.BundleValueError as exc: |
729 errpart = bundler.newpart('error:unsupportedcontent') | 729 errpart = bundler.newpart(b'error:unsupportedcontent') |
730 if exc.parttype is not None: | 730 if exc.parttype is not None: |
731 errpart.addparam('parttype', exc.parttype) | 731 errpart.addparam(b'parttype', exc.parttype) |
732 if exc.params: | 732 if exc.params: |
733 errpart.addparam('params', '\0'.join(exc.params)) | 733 errpart.addparam(b'params', b'\0'.join(exc.params)) |
734 except error.Abort as exc: | 734 except error.Abort as exc: |
735 manargs = [('message', stringutil.forcebytestr(exc))] | 735 manargs = [(b'message', stringutil.forcebytestr(exc))] |
736 advargs = [] | 736 advargs = [] |
737 if exc.hint is not None: | 737 if exc.hint is not None: |
738 advargs.append(('hint', exc.hint)) | 738 advargs.append((b'hint', exc.hint)) |
739 bundler.addpart( | 739 bundler.addpart( |
740 bundle2.bundlepart('error:abort', manargs, advargs) | 740 bundle2.bundlepart(b'error:abort', manargs, advargs) |
741 ) | 741 ) |
742 except error.PushRaced as exc: | 742 except error.PushRaced as exc: |
743 bundler.newpart( | 743 bundler.newpart( |
744 'error:pushraced', | 744 b'error:pushraced', |
745 [('message', stringutil.forcebytestr(exc))], | 745 [(b'message', stringutil.forcebytestr(exc))], |
746 ) | 746 ) |
747 return wireprototypes.streamreslegacy(gen=bundler.getchunks()) | 747 return wireprototypes.streamreslegacy(gen=bundler.getchunks()) |