comparison mercurial/ui.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children 5209fc94b982
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
44 ) 44 )
45 45
46 urlreq = util.urlreq 46 urlreq = util.urlreq
47 47
48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics 48 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
49 _keepalnum = ''.join( 49 _keepalnum = b''.join(
50 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum() 50 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
51 ) 51 )
52 52
53 # The config knobs that will be altered (if unset) by ui.tweakdefaults. 53 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
54 tweakrc = b""" 54 tweakrc = b"""
77 showfunc = 1 77 showfunc = 1
78 word-diff = 1 78 word-diff = 1
79 """ 79 """
80 80
81 samplehgrcs = { 81 samplehgrcs = {
82 'user': b"""# example user config (see 'hg help config' for more info) 82 b'user': b"""# example user config (see 'hg help config' for more info)
83 [ui] 83 [ui]
84 # name and email, e.g. 84 # name and email, e.g.
85 # username = Jane Doe <jdoe@example.com> 85 # username = Jane Doe <jdoe@example.com>
86 username = 86 username =
87 87
104 # 104 #
105 # histedit = 105 # histedit =
106 # rebase = 106 # rebase =
107 # uncommit = 107 # uncommit =
108 """, 108 """,
109 'cloned': b"""# example repository config (see 'hg help config' for more info) 109 b'cloned': b"""# example repository config (see 'hg help config' for more info)
110 [paths] 110 [paths]
111 default = %s 111 default = %s
112 112
113 # path aliases to other clones of this repo in URLs or filesystem paths 113 # path aliases to other clones of this repo in URLs or filesystem paths
114 # (see 'hg help config.paths' for more info) 114 # (see 'hg help config.paths' for more info)
119 119
120 [ui] 120 [ui]
121 # name and email (local to this repository, optional), e.g. 121 # name and email (local to this repository, optional), e.g.
122 # username = Jane Doe <jdoe@example.com> 122 # username = Jane Doe <jdoe@example.com>
123 """, 123 """,
124 'local': b"""# example repository config (see 'hg help config' for more info) 124 b'local': b"""# example repository config (see 'hg help config' for more info)
125 [paths] 125 [paths]
126 # path aliases to other clones of this repo in URLs or filesystem paths 126 # path aliases to other clones of this repo in URLs or filesystem paths
127 # (see 'hg help config.paths' for more info) 127 # (see 'hg help config.paths' for more info)
128 # 128 #
129 # default = http://example.com/hg/example-repo 129 # default = http://example.com/hg/example-repo
133 133
134 [ui] 134 [ui]
135 # name and email (local to this repository, optional), e.g. 135 # name and email (local to this repository, optional), e.g.
136 # username = Jane Doe <jdoe@example.com> 136 # username = Jane Doe <jdoe@example.com>
137 """, 137 """,
138 'global': b"""# example system-wide hg config (see 'hg help config' for more info) 138 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
139 139
140 [ui] 140 [ui]
141 # uncomment to disable color in command output 141 # uncomment to disable color in command output
142 # (see 'hg help color' for details) 142 # (see 'hg help color' for details)
143 # color = never 143 # color = never
283 self.environ = encoding.environ 283 self.environ = encoding.environ
284 284
285 self.httppasswordmgrdb = httppasswordmgrdbproxy() 285 self.httppasswordmgrdb = httppasswordmgrdbproxy()
286 self._blockedtimes = collections.defaultdict(int) 286 self._blockedtimes = collections.defaultdict(int)
287 287
288 allowed = self.configlist('experimental', 'exportableenviron') 288 allowed = self.configlist(b'experimental', b'exportableenviron')
289 if '*' in allowed: 289 if b'*' in allowed:
290 self._exportableenviron = self.environ 290 self._exportableenviron = self.environ
291 else: 291 else:
292 self._exportableenviron = {} 292 self._exportableenviron = {}
293 for k in allowed: 293 for k in allowed:
294 if k in self.environ: 294 if k in self.environ:
298 def load(cls): 298 def load(cls):
299 """Create a ui and load global and user configs""" 299 """Create a ui and load global and user configs"""
300 u = cls() 300 u = cls()
301 # we always trust global config files and environment variables 301 # we always trust global config files and environment variables
302 for t, f in rcutil.rccomponents(): 302 for t, f in rcutil.rccomponents():
303 if t == 'path': 303 if t == b'path':
304 u.readconfig(f, trust=True) 304 u.readconfig(f, trust=True)
305 elif t == 'items': 305 elif t == b'items':
306 sections = set() 306 sections = set()
307 for section, name, value, source in f: 307 for section, name, value, source in f:
308 # do not set u._ocfg 308 # do not set u._ocfg
309 # XXX clean this up once immutable config object is a thing 309 # XXX clean this up once immutable config object is a thing
310 u._tcfg.set(section, name, value, source) 310 u._tcfg.set(section, name, value, source)
311 u._ucfg.set(section, name, value, source) 311 u._ucfg.set(section, name, value, source)
312 sections.add(section) 312 sections.add(section)
313 for section in sections: 313 for section in sections:
314 u.fixconfig(section=section) 314 u.fixconfig(section=section)
315 else: 315 else:
316 raise error.ProgrammingError('unknown rctype: %s' % t) 316 raise error.ProgrammingError(b'unknown rctype: %s' % t)
317 u._maybetweakdefaults() 317 u._maybetweakdefaults()
318 return u 318 return u
319 319
320 def _maybetweakdefaults(self): 320 def _maybetweakdefaults(self):
321 if not self.configbool('ui', 'tweakdefaults'): 321 if not self.configbool(b'ui', b'tweakdefaults'):
322 return 322 return
323 if self._tweaked or self.plain('tweakdefaults'): 323 if self._tweaked or self.plain(b'tweakdefaults'):
324 return 324 return
325 325
326 # Note: it is SUPER IMPORTANT that you set self._tweaked to 326 # Note: it is SUPER IMPORTANT that you set self._tweaked to
327 # True *before* any calls to setconfig(), otherwise you'll get 327 # True *before* any calls to setconfig(), otherwise you'll get
328 # infinite recursion between setconfig and this method. 328 # infinite recursion between setconfig and this method.
329 # 329 #
330 # TODO: We should extract an inner method in setconfig() to 330 # TODO: We should extract an inner method in setconfig() to
331 # avoid this weirdness. 331 # avoid this weirdness.
332 self._tweaked = True 332 self._tweaked = True
333 tmpcfg = config.config() 333 tmpcfg = config.config()
334 tmpcfg.parse('<tweakdefaults>', tweakrc) 334 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
335 for section in tmpcfg: 335 for section in tmpcfg:
336 for name, value in tmpcfg.items(section): 336 for name, value in tmpcfg.items(section):
337 if not self.hasconfig(section, name): 337 if not self.hasconfig(section, name):
338 self.setconfig(section, name, value, "<tweakdefaults>") 338 self.setconfig(section, name, value, b"<tweakdefaults>")
339 339
340 def copy(self): 340 def copy(self):
341 return self.__class__(self) 341 return self.__class__(self)
342 342
343 def resetstate(self): 343 def resetstate(self):
351 # this is open-coded below - search for timeblockedsection to find them 351 # this is open-coded below - search for timeblockedsection to find them
352 starttime = util.timer() 352 starttime = util.timer()
353 try: 353 try:
354 yield 354 yield
355 finally: 355 finally:
356 self._blockedtimes[key + '_blocked'] += ( 356 self._blockedtimes[key + b'_blocked'] += (
357 util.timer() - starttime 357 util.timer() - starttime
358 ) * 1000 358 ) * 1000
359 359
360 @contextlib.contextmanager 360 @contextlib.contextmanager
361 def uninterruptible(self): 361 def uninterruptible(self):
364 Most operations on a repository are safe to interrupt, but a 364 Most operations on a repository are safe to interrupt, but a
365 few are risky (for example repair.strip). This context manager 365 few are risky (for example repair.strip). This context manager
366 lets you advise Mercurial that something risky is happening so 366 lets you advise Mercurial that something risky is happening so
367 that control-C etc can be blocked if desired. 367 that control-C etc can be blocked if desired.
368 """ 368 """
369 enabled = self.configbool('experimental', 'nointerrupt') 369 enabled = self.configbool(b'experimental', b'nointerrupt')
370 if enabled and self.configbool( 370 if enabled and self.configbool(
371 'experimental', 'nointerrupt-interactiveonly' 371 b'experimental', b'nointerrupt-interactiveonly'
372 ): 372 ):
373 enabled = self.interactive() 373 enabled = self.interactive()
374 if self._uninterruptible or not enabled: 374 if self._uninterruptible or not enabled:
375 # if nointerrupt support is turned off, the process isn't 375 # if nointerrupt support is turned off, the process isn't
376 # interactive, or we're already in an uninterruptible 376 # interactive, or we're already in an uninterruptible
377 # block, do nothing. 377 # block, do nothing.
378 yield 378 yield
379 return 379 return
380 380
381 def warn(): 381 def warn():
382 self.warn(_("shutting down cleanly\n")) 382 self.warn(_(b"shutting down cleanly\n"))
383 self.warn( 383 self.warn(
384 _("press ^C again to terminate immediately (dangerous)\n") 384 _(b"press ^C again to terminate immediately (dangerous)\n")
385 ) 385 )
386 return True 386 return True
387 387
388 with procutil.uninterruptible(warn): 388 with procutil.uninterruptible(warn):
389 try: 389 try:
399 st = util.fstat(fp) 399 st = util.fstat(fp)
400 if util.isowner(st): 400 if util.isowner(st):
401 return True 401 return True
402 402
403 tusers, tgroups = self._trustusers, self._trustgroups 403 tusers, tgroups = self._trustusers, self._trustgroups
404 if '*' in tusers or '*' in tgroups: 404 if b'*' in tusers or b'*' in tgroups:
405 return True 405 return True
406 406
407 user = util.username(st.st_uid) 407 user = util.username(st.st_uid)
408 group = util.groupname(st.st_gid) 408 group = util.groupname(st.st_gid)
409 if user in tusers or group in tgroups or user == util.username(): 409 if user in tusers or group in tgroups or user == util.username():
410 return True 410 return True
411 411
412 if self._reportuntrusted: 412 if self._reportuntrusted:
413 self.warn( 413 self.warn(
414 _('not trusting file %s from untrusted ' 'user %s, group %s\n') 414 _(
415 b'not trusting file %s from untrusted '
416 b'user %s, group %s\n'
417 )
415 % (f, user, group) 418 % (f, user, group)
416 ) 419 )
417 return False 420 return False
418 421
419 def readconfig( 422 def readconfig(
433 cfg.read(filename, fp, sections=sections, remap=remap) 436 cfg.read(filename, fp, sections=sections, remap=remap)
434 fp.close() 437 fp.close()
435 except error.ConfigError as inst: 438 except error.ConfigError as inst:
436 if trusted: 439 if trusted:
437 raise 440 raise
438 self.warn(_("ignored: %s\n") % stringutil.forcebytestr(inst)) 441 self.warn(_(b"ignored: %s\n") % stringutil.forcebytestr(inst))
439 442
440 if self.plain(): 443 if self.plain():
441 for k in ( 444 for k in (
442 'debug', 445 b'debug',
443 'fallbackencoding', 446 b'fallbackencoding',
444 'quiet', 447 b'quiet',
445 'slash', 448 b'slash',
446 'logtemplate', 449 b'logtemplate',
447 'message-output', 450 b'message-output',
448 'statuscopies', 451 b'statuscopies',
449 'style', 452 b'style',
450 'traceback', 453 b'traceback',
451 'verbose', 454 b'verbose',
452 ): 455 ):
453 if k in cfg['ui']: 456 if k in cfg[b'ui']:
454 del cfg['ui'][k] 457 del cfg[b'ui'][k]
455 for k, v in cfg.items('defaults'): 458 for k, v in cfg.items(b'defaults'):
456 del cfg['defaults'][k] 459 del cfg[b'defaults'][k]
457 for k, v in cfg.items('commands'): 460 for k, v in cfg.items(b'commands'):
458 del cfg['commands'][k] 461 del cfg[b'commands'][k]
459 # Don't remove aliases from the configuration if in the exceptionlist 462 # Don't remove aliases from the configuration if in the exceptionlist
460 if self.plain('alias'): 463 if self.plain(b'alias'):
461 for k, v in cfg.items('alias'): 464 for k, v in cfg.items(b'alias'):
462 del cfg['alias'][k] 465 del cfg[b'alias'][k]
463 if self.plain('revsetalias'): 466 if self.plain(b'revsetalias'):
464 for k, v in cfg.items('revsetalias'): 467 for k, v in cfg.items(b'revsetalias'):
465 del cfg['revsetalias'][k] 468 del cfg[b'revsetalias'][k]
466 if self.plain('templatealias'): 469 if self.plain(b'templatealias'):
467 for k, v in cfg.items('templatealias'): 470 for k, v in cfg.items(b'templatealias'):
468 del cfg['templatealias'][k] 471 del cfg[b'templatealias'][k]
469 472
470 if trusted: 473 if trusted:
471 self._tcfg.update(cfg) 474 self._tcfg.update(cfg)
472 self._tcfg.update(self._ocfg) 475 self._tcfg.update(self._ocfg)
473 self._ucfg.update(cfg) 476 self._ucfg.update(cfg)
474 self._ucfg.update(self._ocfg) 477 self._ucfg.update(self._ocfg)
475 478
476 if root is None: 479 if root is None:
477 root = os.path.expanduser('~') 480 root = os.path.expanduser(b'~')
478 self.fixconfig(root=root) 481 self.fixconfig(root=root)
479 482
480 def fixconfig(self, root=None, section=None): 483 def fixconfig(self, root=None, section=None):
481 if section in (None, 'paths'): 484 if section in (None, b'paths'):
482 # expand vars and ~ 485 # expand vars and ~
483 # translate paths relative to root (or home) into absolute paths 486 # translate paths relative to root (or home) into absolute paths
484 root = root or encoding.getcwd() 487 root = root or encoding.getcwd()
485 for c in self._tcfg, self._ucfg, self._ocfg: 488 for c in self._tcfg, self._ucfg, self._ocfg:
486 for n, p in c.items('paths'): 489 for n, p in c.items(b'paths'):
487 # Ignore sub-options. 490 # Ignore sub-options.
488 if ':' in n: 491 if b':' in n:
489 continue 492 continue
490 if not p: 493 if not p:
491 continue 494 continue
492 if '%%' in p: 495 if b'%%' in p:
493 s = self.configsource('paths', n) or 'none' 496 s = self.configsource(b'paths', n) or b'none'
494 self.warn( 497 self.warn(
495 _("(deprecated '%%' in path %s=%s from %s)\n") 498 _(b"(deprecated '%%' in path %s=%s from %s)\n")
496 % (n, p, s) 499 % (n, p, s)
497 ) 500 )
498 p = p.replace('%%', '%') 501 p = p.replace(b'%%', b'%')
499 p = util.expandpath(p) 502 p = util.expandpath(p)
500 if not util.hasscheme(p) and not os.path.isabs(p): 503 if not util.hasscheme(p) and not os.path.isabs(p):
501 p = os.path.normpath(os.path.join(root, p)) 504 p = os.path.normpath(os.path.join(root, p))
502 c.set("paths", n, p) 505 c.set(b"paths", n, p)
503 506
504 if section in (None, 'ui'): 507 if section in (None, b'ui'):
505 # update ui options 508 # update ui options
506 self._fmsgout, self._fmsgerr = _selectmsgdests(self) 509 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
507 self.debugflag = self.configbool('ui', 'debug') 510 self.debugflag = self.configbool(b'ui', b'debug')
508 self.verbose = self.debugflag or self.configbool('ui', 'verbose') 511 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
509 self.quiet = not self.debugflag and self.configbool('ui', 'quiet') 512 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
510 if self.verbose and self.quiet: 513 if self.verbose and self.quiet:
511 self.quiet = self.verbose = False 514 self.quiet = self.verbose = False
512 self._reportuntrusted = self.debugflag or self.configbool( 515 self._reportuntrusted = self.debugflag or self.configbool(
513 "ui", "report_untrusted" 516 b"ui", b"report_untrusted"
514 ) 517 )
515 self.tracebackflag = self.configbool('ui', 'traceback') 518 self.tracebackflag = self.configbool(b'ui', b'traceback')
516 self.logblockedtimes = self.configbool('ui', 'logblockedtimes') 519 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
517 520
518 if section in (None, 'trusted'): 521 if section in (None, b'trusted'):
519 # update trust information 522 # update trust information
520 self._trustusers.update(self.configlist('trusted', 'users')) 523 self._trustusers.update(self.configlist(b'trusted', b'users'))
521 self._trustgroups.update(self.configlist('trusted', 'groups')) 524 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
522 525
523 if section in (None, b'devel', b'ui') and self.debugflag: 526 if section in (None, b'devel', b'ui') and self.debugflag:
524 tracked = set() 527 tracked = set()
525 if self.configbool(b'devel', b'debug.extensions'): 528 if self.configbool(b'devel', b'debug.extensions'):
526 tracked.add(b'extension') 529 tracked.add(b'extension')
538 def restoreconfig(self, data): 541 def restoreconfig(self, data):
539 self._ocfg.restore(data[0]) 542 self._ocfg.restore(data[0])
540 self._tcfg.restore(data[1]) 543 self._tcfg.restore(data[1])
541 self._ucfg.restore(data[2]) 544 self._ucfg.restore(data[2])
542 545
543 def setconfig(self, section, name, value, source=''): 546 def setconfig(self, section, name, value, source=b''):
544 for cfg in (self._ocfg, self._tcfg, self._ucfg): 547 for cfg in (self._ocfg, self._tcfg, self._ucfg):
545 cfg.set(section, name, value, source) 548 cfg.set(section, name, value, source)
546 self.fixconfig(section=section) 549 self.fixconfig(section=section)
547 self._maybetweakdefaults() 550 self._maybetweakdefaults()
548 551
571 if callable(item.default): 574 if callable(item.default):
572 itemdefault = item.default() 575 itemdefault = item.default()
573 else: 576 else:
574 itemdefault = item.default 577 itemdefault = item.default
575 else: 578 else:
576 msg = "accessing unregistered config item: '%s.%s'" 579 msg = b"accessing unregistered config item: '%s.%s'"
577 msg %= (section, name) 580 msg %= (section, name)
578 self.develwarn(msg, 2, 'warn-config-unknown') 581 self.develwarn(msg, 2, b'warn-config-unknown')
579 582
580 if default is _unset: 583 if default is _unset:
581 if item is None: 584 if item is None:
582 value = default 585 value = default
583 elif item.default is configitems.dynamicdefault: 586 elif item.default is configitems.dynamicdefault:
584 value = None 587 value = None
585 msg = "config item requires an explicit default value: '%s.%s'" 588 msg = b"config item requires an explicit default value: '%s.%s'"
586 msg %= (section, name) 589 msg %= (section, name)
587 self.develwarn(msg, 2, 'warn-config-default') 590 self.develwarn(msg, 2, b'warn-config-default')
588 else: 591 else:
589 value = itemdefault 592 value = itemdefault
590 elif ( 593 elif (
591 item is not None 594 item is not None
592 and item.default is not configitems.dynamicdefault 595 and item.default is not configitems.dynamicdefault
593 and default != itemdefault 596 and default != itemdefault
594 ): 597 ):
595 msg = ( 598 msg = (
596 "specifying a mismatched default value for a registered " 599 b"specifying a mismatched default value for a registered "
597 "config item: '%s.%s' '%s'" 600 b"config item: '%s.%s' '%s'"
598 ) 601 )
599 msg %= (section, name, pycompat.bytestr(default)) 602 msg %= (section, name, pycompat.bytestr(default))
600 self.develwarn(msg, 2, 'warn-config-default') 603 self.develwarn(msg, 2, b'warn-config-default')
601 604
602 for s, n in alternates: 605 for s, n in alternates:
603 candidate = self._data(untrusted).get(s, n, None) 606 candidate = self._data(untrusted).get(s, n, None)
604 if candidate is not None: 607 if candidate is not None:
605 value = candidate 608 value = candidate
608 if self.debugflag and not untrusted and self._reportuntrusted: 611 if self.debugflag and not untrusted and self._reportuntrusted:
609 for s, n in alternates: 612 for s, n in alternates:
610 uvalue = self._ucfg.get(s, n) 613 uvalue = self._ucfg.get(s, n)
611 if uvalue is not None and uvalue != value: 614 if uvalue is not None and uvalue != value:
612 self.debug( 615 self.debug(
613 "ignoring untrusted configuration option " 616 b"ignoring untrusted configuration option "
614 "%s.%s = %s\n" % (s, n, uvalue) 617 b"%s.%s = %s\n" % (s, n, uvalue)
615 ) 618 )
616 return value 619 return value
617 620
618 def configsuboptions(self, section, name, default=_unset, untrusted=False): 621 def configsuboptions(self, section, name, default=_unset, untrusted=False):
619 """Get a config option and all sub-options. 622 """Get a config option and all sub-options.
626 is a dict of defined sub-options where keys and values are strings. 629 is a dict of defined sub-options where keys and values are strings.
627 """ 630 """
628 main = self.config(section, name, default, untrusted=untrusted) 631 main = self.config(section, name, default, untrusted=untrusted)
629 data = self._data(untrusted) 632 data = self._data(untrusted)
630 sub = {} 633 sub = {}
631 prefix = '%s:' % name 634 prefix = b'%s:' % name
632 for k, v in data.items(section): 635 for k, v in data.items(section):
633 if k.startswith(prefix): 636 if k.startswith(prefix):
634 sub[k[len(prefix) :]] = v 637 sub[k[len(prefix) :]] = v
635 638
636 if self.debugflag and not untrusted and self._reportuntrusted: 639 if self.debugflag and not untrusted and self._reportuntrusted:
637 for k, v in sub.items(): 640 for k, v in sub.items():
638 uvalue = self._ucfg.get(section, '%s:%s' % (name, k)) 641 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
639 if uvalue is not None and uvalue != v: 642 if uvalue is not None and uvalue != v:
640 self.debug( 643 self.debug(
641 'ignoring untrusted configuration option ' 644 b'ignoring untrusted configuration option '
642 '%s:%s.%s = %s\n' % (section, name, k, uvalue) 645 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
643 ) 646 )
644 647
645 return main, sub 648 return main, sub
646 649
647 def configpath(self, section, name, default=_unset, untrusted=False): 650 def configpath(self, section, name, default=_unset, untrusted=False):
648 'get a path config item, expanded relative to repo root or config file' 651 b'get a path config item, expanded relative to repo root or config file'
649 v = self.config(section, name, default, untrusted) 652 v = self.config(section, name, default, untrusted)
650 if v is None: 653 if v is None:
651 return None 654 return None
652 if not os.path.isabs(v) or "://" not in v: 655 if not os.path.isabs(v) or b"://" not in v:
653 src = self.configsource(section, name, untrusted) 656 src = self.configsource(section, name, untrusted)
654 if ':' in src: 657 if b':' in src:
655 base = os.path.dirname(src.rsplit(':')[0]) 658 base = os.path.dirname(src.rsplit(b':')[0])
656 v = os.path.join(base, os.path.expanduser(v)) 659 v = os.path.join(base, os.path.expanduser(v))
657 return v 660 return v
658 661
659 def configbool(self, section, name, default=_unset, untrusted=False): 662 def configbool(self, section, name, default=_unset, untrusted=False):
660 """parse a configuration element as a boolean 663 """parse a configuration element as a boolean
687 if isinstance(v, bool): 690 if isinstance(v, bool):
688 return v 691 return v
689 b = stringutil.parsebool(v) 692 b = stringutil.parsebool(v)
690 if b is None: 693 if b is None:
691 raise error.ConfigError( 694 raise error.ConfigError(
692 _("%s.%s is not a boolean ('%s')") % (section, name, v) 695 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
693 ) 696 )
694 return b 697 return b
695 698
696 def configwith( 699 def configwith(
697 self, convert, section, name, default=_unset, desc=None, untrusted=False 700 self, convert, section, name, default=_unset, desc=None, untrusted=False
725 return convert(v) 728 return convert(v)
726 except (ValueError, error.ParseError): 729 except (ValueError, error.ParseError):
727 if desc is None: 730 if desc is None:
728 desc = pycompat.sysbytes(convert.__name__) 731 desc = pycompat.sysbytes(convert.__name__)
729 raise error.ConfigError( 732 raise error.ConfigError(
730 _("%s.%s is not a valid %s ('%s')") % (section, name, desc, v) 733 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
731 ) 734 )
732 735
733 def configint(self, section, name, default=_unset, untrusted=False): 736 def configint(self, section, name, default=_unset, untrusted=False):
734 """parse a configuration element as an integer 737 """parse a configuration element as an integer
735 738
748 ... 751 ...
749 ConfigError: foo.invalid is not a valid integer ('somevalue') 752 ConfigError: foo.invalid is not a valid integer ('somevalue')
750 """ 753 """
751 754
752 return self.configwith( 755 return self.configwith(
753 int, section, name, default, 'integer', untrusted 756 int, section, name, default, b'integer', untrusted
754 ) 757 )
755 758
756 def configbytes(self, section, name, default=_unset, untrusted=False): 759 def configbytes(self, section, name, default=_unset, untrusted=False):
757 """parse a configuration element as a quantity in bytes 760 """parse a configuration element as a quantity in bytes
758 761
784 return value 787 return value
785 try: 788 try:
786 return util.sizetoint(value) 789 return util.sizetoint(value)
787 except error.ParseError: 790 except error.ParseError:
788 raise error.ConfigError( 791 raise error.ConfigError(
789 _("%s.%s is not a byte quantity ('%s')") 792 _(b"%s.%s is not a byte quantity ('%s')")
790 % (section, name, value) 793 % (section, name, value)
791 ) 794 )
792 795
793 def configlist(self, section, name, default=_unset, untrusted=False): 796 def configlist(self, section, name, default=_unset, untrusted=False):
794 """parse a configuration element as a list of comma/space separated 797 """parse a configuration element as a list of comma/space separated
802 >>> u.configlist(s, b'list2') 805 >>> u.configlist(s, b'list2')
803 ['this', 'is', 'a small', 'test'] 806 ['this', 'is', 'a small', 'test']
804 """ 807 """
805 # default is not always a list 808 # default is not always a list
806 v = self.configwith( 809 v = self.configwith(
807 config.parselist, section, name, default, 'list', untrusted 810 config.parselist, section, name, default, b'list', untrusted
808 ) 811 )
809 if isinstance(v, bytes): 812 if isinstance(v, bytes):
810 return config.parselist(v) 813 return config.parselist(v)
811 elif v is None: 814 elif v is None:
812 return [] 815 return []
820 >>> u.configdate(s, b'date') 823 >>> u.configdate(s, b'date')
821 (0, 0) 824 (0, 0)
822 """ 825 """
823 if self.config(section, name, default, untrusted): 826 if self.config(section, name, default, untrusted):
824 return self.configwith( 827 return self.configwith(
825 dateutil.parsedate, section, name, default, 'date', untrusted 828 dateutil.parsedate, section, name, default, b'date', untrusted
826 ) 829 )
827 if default is _unset: 830 if default is _unset:
828 return None 831 return None
829 return default 832 return default
830 833
847 return section in self._data(untrusted) 850 return section in self._data(untrusted)
848 851
849 def configitems(self, section, untrusted=False, ignoresub=False): 852 def configitems(self, section, untrusted=False, ignoresub=False):
850 items = self._data(untrusted).items(section) 853 items = self._data(untrusted).items(section)
851 if ignoresub: 854 if ignoresub:
852 items = [i for i in items if ':' not in i[0]] 855 items = [i for i in items if b':' not in i[0]]
853 if self.debugflag and not untrusted and self._reportuntrusted: 856 if self.debugflag and not untrusted and self._reportuntrusted:
854 for k, v in self._ucfg.items(section): 857 for k, v in self._ucfg.items(section):
855 if self._tcfg.get(section, k) != v: 858 if self._tcfg.get(section, k) != v:
856 self.debug( 859 self.debug(
857 "ignoring untrusted configuration option " 860 b"ignoring untrusted configuration option "
858 "%s.%s = %s\n" % (section, k, v) 861 b"%s.%s = %s\n" % (section, k, v)
859 ) 862 )
860 return items 863 return items
861 864
862 def walkconfig(self, untrusted=False): 865 def walkconfig(self, untrusted=False):
863 cfg = self._data(untrusted) 866 cfg = self._data(untrusted)
880 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT 883 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
881 - False if feature is disabled by default and not included in HGPLAIN 884 - False if feature is disabled by default and not included in HGPLAIN
882 - True otherwise 885 - True otherwise
883 ''' 886 '''
884 if ( 887 if (
885 'HGPLAIN' not in encoding.environ 888 b'HGPLAIN' not in encoding.environ
886 and 'HGPLAINEXCEPT' not in encoding.environ 889 and b'HGPLAINEXCEPT' not in encoding.environ
887 ): 890 ):
888 return False 891 return False
889 exceptions = ( 892 exceptions = (
890 encoding.environ.get('HGPLAINEXCEPT', '').strip().split(',') 893 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
891 ) 894 )
892 # TODO: add support for HGPLAIN=+feature,-feature syntax 895 # TODO: add support for HGPLAIN=+feature,-feature syntax
893 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','): 896 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
894 exceptions.append('strictflags') 897 b','
898 ):
899 exceptions.append(b'strictflags')
895 if feature and exceptions: 900 if feature and exceptions:
896 return feature not in exceptions 901 return feature not in exceptions
897 return True 902 return True
898 903
899 def username(self, acceptempty=False): 904 def username(self, acceptempty=False):
904 If not found and acceptempty is True, returns None. 909 If not found and acceptempty is True, returns None.
905 If not found and ui.askusername is True, ask the user, else use 910 If not found and ui.askusername is True, ask the user, else use
906 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname". 911 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
907 If no username could be found, raise an Abort error. 912 If no username could be found, raise an Abort error.
908 """ 913 """
909 user = encoding.environ.get("HGUSER") 914 user = encoding.environ.get(b"HGUSER")
910 if user is None: 915 if user is None:
911 user = self.config("ui", "username") 916 user = self.config(b"ui", b"username")
912 if user is not None: 917 if user is not None:
913 user = os.path.expandvars(user) 918 user = os.path.expandvars(user)
914 if user is None: 919 if user is None:
915 user = encoding.environ.get("EMAIL") 920 user = encoding.environ.get(b"EMAIL")
916 if user is None and acceptempty: 921 if user is None and acceptempty:
917 return user 922 return user
918 if user is None and self.configbool("ui", "askusername"): 923 if user is None and self.configbool(b"ui", b"askusername"):
919 user = self.prompt(_("enter a commit username:"), default=None) 924 user = self.prompt(_(b"enter a commit username:"), default=None)
920 if user is None and not self.interactive(): 925 if user is None and not self.interactive():
921 try: 926 try:
922 user = '%s@%s' % ( 927 user = b'%s@%s' % (
923 procutil.getuser(), 928 procutil.getuser(),
924 encoding.strtolocal(socket.getfqdn()), 929 encoding.strtolocal(socket.getfqdn()),
925 ) 930 )
926 self.warn(_("no username found, using '%s' instead\n") % user) 931 self.warn(_(b"no username found, using '%s' instead\n") % user)
927 except KeyError: 932 except KeyError:
928 pass 933 pass
929 if not user: 934 if not user:
930 raise error.Abort( 935 raise error.Abort(
931 _('no username supplied'), 936 _(b'no username supplied'),
932 hint=_("use 'hg config --edit' " 'to set your username'), 937 hint=_(b"use 'hg config --edit' " b'to set your username'),
933 ) 938 )
934 if "\n" in user: 939 if b"\n" in user:
935 raise error.Abort( 940 raise error.Abort(
936 _("username %r contains a newline\n") % pycompat.bytestr(user) 941 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
937 ) 942 )
938 return user 943 return user
939 944
940 def shortuser(self, user): 945 def shortuser(self, user):
941 """Return a short representation of a user name or email address.""" 946 """Return a short representation of a user name or email address."""
1028 if self._bufferstates: 1033 if self._bufferstates:
1029 self._bufferapplylabels = self._bufferstates[-1][2] 1034 self._bufferapplylabels = self._bufferstates[-1][2]
1030 else: 1035 else:
1031 self._bufferapplylabels = None 1036 self._bufferapplylabels = None
1032 1037
1033 return "".join(self._buffers.pop()) 1038 return b"".join(self._buffers.pop())
1034 1039
1035 def _isbuffered(self, dest): 1040 def _isbuffered(self, dest):
1036 if dest is self._fout: 1041 if dest is self._fout:
1037 return bool(self._buffers) 1042 return bool(self._buffers)
1038 if dest is self._ferr: 1043 if dest is self._ferr:
1046 return self._colormode is None 1051 return self._colormode is None
1047 1052
1048 def canbatchlabeledwrites(self): 1053 def canbatchlabeledwrites(self):
1049 '''check if write calls with labels are batchable''' 1054 '''check if write calls with labels are batchable'''
1050 # Windows color printing is special, see ``write``. 1055 # Windows color printing is special, see ``write``.
1051 return self._colormode != 'win32' 1056 return self._colormode != b'win32'
1052 1057
1053 def write(self, *args, **opts): 1058 def write(self, *args, **opts):
1054 '''write args to output 1059 '''write args to output
1055 1060
1056 By default, this method simply writes to the buffer or stdout. 1061 By default, this method simply writes to the buffer or stdout.
1068 ''' 1073 '''
1069 dest = self._fout 1074 dest = self._fout
1070 1075
1071 # inlined _write() for speed 1076 # inlined _write() for speed
1072 if self._buffers: 1077 if self._buffers:
1073 label = opts.get(r'label', '') 1078 label = opts.get(r'label', b'')
1074 if label and self._bufferapplylabels: 1079 if label and self._bufferapplylabels:
1075 self._buffers[-1].extend(self.label(a, label) for a in args) 1080 self._buffers[-1].extend(self.label(a, label) for a in args)
1076 else: 1081 else:
1077 self._buffers[-1].extend(args) 1082 self._buffers[-1].extend(args)
1078 return 1083 return
1082 msg = b''.join(args) 1087 msg = b''.join(args)
1083 1088
1084 # opencode timeblockedsection because this is a critical path 1089 # opencode timeblockedsection because this is a critical path
1085 starttime = util.timer() 1090 starttime = util.timer()
1086 try: 1091 try:
1087 if self._colormode == 'win32': 1092 if self._colormode == b'win32':
1088 # windows color printing is its own can of crab, defer to 1093 # windows color printing is its own can of crab, defer to
1089 # the color module and that is it. 1094 # the color module and that is it.
1090 color.win32print(self, dest.write, msg, **opts) 1095 color.win32print(self, dest.write, msg, **opts)
1091 else: 1096 else:
1092 if self._colormode is not None: 1097 if self._colormode is not None:
1093 label = opts.get(r'label', '') 1098 label = opts.get(r'label', b'')
1094 msg = self.label(msg, label) 1099 msg = self.label(msg, label)
1095 dest.write(msg) 1100 dest.write(msg)
1096 except IOError as err: 1101 except IOError as err:
1097 raise error.StdioError(err) 1102 raise error.StdioError(err)
1098 finally: 1103 finally:
1099 self._blockedtimes['stdio_blocked'] += ( 1104 self._blockedtimes[b'stdio_blocked'] += (
1100 util.timer() - starttime 1105 util.timer() - starttime
1101 ) * 1000 1106 ) * 1000
1102 1107
1103 def write_err(self, *args, **opts): 1108 def write_err(self, *args, **opts):
1104 self._write(self._ferr, *args, **opts) 1109 self._write(self._ferr, *args, **opts)
1105 1110
1106 def _write(self, dest, *args, **opts): 1111 def _write(self, dest, *args, **opts):
1107 # update write() as well if you touch this code 1112 # update write() as well if you touch this code
1108 if self._isbuffered(dest): 1113 if self._isbuffered(dest):
1109 label = opts.get(r'label', '') 1114 label = opts.get(r'label', b'')
1110 if label and self._bufferapplylabels: 1115 if label and self._bufferapplylabels:
1111 self._buffers[-1].extend(self.label(a, label) for a in args) 1116 self._buffers[-1].extend(self.label(a, label) for a in args)
1112 else: 1117 else:
1113 self._buffers[-1].extend(args) 1118 self._buffers[-1].extend(args)
1114 else: 1119 else:
1126 self._fout.flush() 1131 self._fout.flush()
1127 if getattr(dest, 'structured', False): 1132 if getattr(dest, 'structured', False):
1128 # channel for machine-readable output with metadata, where 1133 # channel for machine-readable output with metadata, where
1129 # no extra colorization is necessary. 1134 # no extra colorization is necessary.
1130 dest.write(msg, **opts) 1135 dest.write(msg, **opts)
1131 elif self._colormode == 'win32': 1136 elif self._colormode == b'win32':
1132 # windows color printing is its own can of crab, defer to 1137 # windows color printing is its own can of crab, defer to
1133 # the color module and that is it. 1138 # the color module and that is it.
1134 color.win32print(self, dest.write, msg, **opts) 1139 color.win32print(self, dest.write, msg, **opts)
1135 else: 1140 else:
1136 if self._colormode is not None: 1141 if self._colormode is not None:
1137 label = opts.get(r'label', '') 1142 label = opts.get(r'label', b'')
1138 msg = self.label(msg, label) 1143 msg = self.label(msg, label)
1139 dest.write(msg) 1144 dest.write(msg)
1140 # stderr may be buffered under win32 when redirected to files, 1145 # stderr may be buffered under win32 when redirected to files,
1141 # including stdout. 1146 # including stdout.
1142 if dest is self._ferr and not getattr(self._ferr, 'closed', False): 1147 if dest is self._ferr and not getattr(self._ferr, 'closed', False):
1149 ): 1154 ):
1150 # no way to report the error, so ignore it 1155 # no way to report the error, so ignore it
1151 return 1156 return
1152 raise error.StdioError(err) 1157 raise error.StdioError(err)
1153 finally: 1158 finally:
1154 self._blockedtimes['stdio_blocked'] += ( 1159 self._blockedtimes[b'stdio_blocked'] += (
1155 util.timer() - starttime 1160 util.timer() - starttime
1156 ) * 1000 1161 ) * 1000
1157 1162
1158 def _writemsg(self, dest, *args, **opts): 1163 def _writemsg(self, dest, *args, **opts):
1159 _writemsgwith(self._write, dest, *args, **opts) 1164 _writemsgwith(self._write, dest, *args, **opts)
1175 self._ferr.flush() 1180 self._ferr.flush()
1176 except IOError as err: 1181 except IOError as err:
1177 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF): 1182 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1178 raise error.StdioError(err) 1183 raise error.StdioError(err)
1179 finally: 1184 finally:
1180 self._blockedtimes['stdio_blocked'] += ( 1185 self._blockedtimes[b'stdio_blocked'] += (
1181 util.timer() - starttime 1186 util.timer() - starttime
1182 ) * 1000 1187 ) * 1000
1183 1188
1184 def _isatty(self, fh): 1189 def _isatty(self, fh):
1185 if self.configbool('ui', 'nontty'): 1190 if self.configbool(b'ui', b'nontty'):
1186 return False 1191 return False
1187 return procutil.isatty(fh) 1192 return procutil.isatty(fh)
1188 1193
1189 def protectfinout(self): 1194 def protectfinout(self):
1190 """Duplicate ui streams and redirect original if they are stdio 1195 """Duplicate ui streams and redirect original if they are stdio
1237 """ 1242 """
1238 if self._disablepager or self.pageractive: 1243 if self._disablepager or self.pageractive:
1239 # how pager should do is already determined 1244 # how pager should do is already determined
1240 return 1245 return
1241 1246
1242 if not command.startswith('internal-always-') and ( 1247 if not command.startswith(b'internal-always-') and (
1243 # explicit --pager=on (= 'internal-always-' prefix) should 1248 # explicit --pager=on (= 'internal-always-' prefix) should
1244 # take precedence over disabling factors below 1249 # take precedence over disabling factors below
1245 command in self.configlist('pager', 'ignore') 1250 command in self.configlist(b'pager', b'ignore')
1246 or not self.configbool('ui', 'paginate') 1251 or not self.configbool(b'ui', b'paginate')
1247 or not self.configbool('pager', 'attend-' + command, True) 1252 or not self.configbool(b'pager', b'attend-' + command, True)
1248 or encoding.environ.get('TERM') == 'dumb' 1253 or encoding.environ.get(b'TERM') == b'dumb'
1249 # TODO: if we want to allow HGPLAINEXCEPT=pager, 1254 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1250 # formatted() will need some adjustment. 1255 # formatted() will need some adjustment.
1251 or not self.formatted() 1256 or not self.formatted()
1252 or self.plain() 1257 or self.plain()
1253 or self._buffers 1258 or self._buffers
1254 # TODO: expose debugger-enabled on the UI object 1259 # TODO: expose debugger-enabled on the UI object
1255 or '--debugger' in pycompat.sysargv 1260 or b'--debugger' in pycompat.sysargv
1256 ): 1261 ):
1257 # We only want to paginate if the ui appears to be 1262 # We only want to paginate if the ui appears to be
1258 # interactive, the user didn't say HGPLAIN or 1263 # interactive, the user didn't say HGPLAIN or
1259 # HGPLAINEXCEPT=pager, and the user didn't specify --debug. 1264 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1260 return 1265 return
1261 1266
1262 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager) 1267 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1263 if not pagercmd: 1268 if not pagercmd:
1264 return 1269 return
1265 1270
1266 pagerenv = {} 1271 pagerenv = {}
1267 for name, value in rcutil.defaultpagerenv().items(): 1272 for name, value in rcutil.defaultpagerenv().items():
1268 if name not in encoding.environ: 1273 if name not in encoding.environ:
1269 pagerenv[name] = value 1274 pagerenv[name] = value
1270 1275
1271 self.debug( 1276 self.debug(
1272 'starting pager for command %s\n' % stringutil.pprint(command) 1277 b'starting pager for command %s\n' % stringutil.pprint(command)
1273 ) 1278 )
1274 self.flush() 1279 self.flush()
1275 1280
1276 wasformatted = self.formatted() 1281 wasformatted = self.formatted()
1277 if util.safehasattr(signal, "SIGPIPE"): 1282 if util.safehasattr(signal, b"SIGPIPE"):
1278 signal.signal(signal.SIGPIPE, _catchterm) 1283 signal.signal(signal.SIGPIPE, _catchterm)
1279 if self._runpager(pagercmd, pagerenv): 1284 if self._runpager(pagercmd, pagerenv):
1280 self.pageractive = True 1285 self.pageractive = True
1281 # Preserve the formatted-ness of the UI. This is important 1286 # Preserve the formatted-ness of the UI. This is important
1282 # because we mess with stdout, which might confuse 1287 # because we mess with stdout, which might confuse
1283 # auto-detection of things being formatted. 1288 # auto-detection of things being formatted.
1284 self.setconfig('ui', 'formatted', wasformatted, 'pager') 1289 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1285 self.setconfig('ui', 'interactive', False, 'pager') 1290 self.setconfig(b'ui', b'interactive', False, b'pager')
1286 1291
1287 # If pagermode differs from color.mode, reconfigure color now that 1292 # If pagermode differs from color.mode, reconfigure color now that
1288 # pageractive is set. 1293 # pageractive is set.
1289 cm = self._colormode 1294 cm = self._colormode
1290 if cm != self.config('color', 'pagermode', cm): 1295 if cm != self.config(b'color', b'pagermode', cm):
1291 color.setup(self) 1296 color.setup(self)
1292 else: 1297 else:
1293 # If the pager can't be spawned in dispatch when --pager=on is 1298 # If the pager can't be spawned in dispatch when --pager=on is
1294 # given, don't try again when the command runs, to avoid a duplicate 1299 # given, don't try again when the command runs, to avoid a duplicate
1295 # warning about a missing pager command. 1300 # warning about a missing pager command.
1299 """Actually start the pager and set up file descriptors. 1304 """Actually start the pager and set up file descriptors.
1300 1305
1301 This is separate in part so that extensions (like chg) can 1306 This is separate in part so that extensions (like chg) can
1302 override how a pager is invoked. 1307 override how a pager is invoked.
1303 """ 1308 """
1304 if command == 'cat': 1309 if command == b'cat':
1305 # Save ourselves some work. 1310 # Save ourselves some work.
1306 return False 1311 return False
1307 # If the command doesn't contain any of these characters, we 1312 # If the command doesn't contain any of these characters, we
1308 # assume it's a binary and exec it directly. This means for 1313 # assume it's a binary and exec it directly. This means for
1309 # simple pager command configurations, we can degrade 1314 # simple pager command configurations, we can degrade
1310 # gracefully and tell the user about their broken pager. 1315 # gracefully and tell the user about their broken pager.
1311 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%") 1316 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1312 1317
1313 if pycompat.iswindows and not shell: 1318 if pycompat.iswindows and not shell:
1314 # Window's built-in `more` cannot be invoked with shell=False, but 1319 # Window's built-in `more` cannot be invoked with shell=False, but
1315 # its `more.com` can. Hide this implementation detail from the 1320 # its `more.com` can. Hide this implementation detail from the
1316 # user so we can also get sane bad PAGER behavior. MSYS has 1321 # user so we can also get sane bad PAGER behavior. MSYS has
1317 # `more.exe`, so do a cmd.exe style resolution of the executable to 1322 # `more.exe`, so do a cmd.exe style resolution of the executable to
1318 # determine which one to use. 1323 # determine which one to use.
1319 fullcmd = procutil.findexe(command) 1324 fullcmd = procutil.findexe(command)
1320 if not fullcmd: 1325 if not fullcmd:
1321 self.warn( 1326 self.warn(
1322 _("missing pager command '%s', skipping pager\n") % command 1327 _(b"missing pager command '%s', skipping pager\n") % command
1323 ) 1328 )
1324 return False 1329 return False
1325 1330
1326 command = fullcmd 1331 command = fullcmd
1327 1332
1337 env=procutil.tonativeenv(procutil.shellenviron(env)), 1342 env=procutil.tonativeenv(procutil.shellenviron(env)),
1338 ) 1343 )
1339 except OSError as e: 1344 except OSError as e:
1340 if e.errno == errno.ENOENT and not shell: 1345 if e.errno == errno.ENOENT and not shell:
1341 self.warn( 1346 self.warn(
1342 _("missing pager command '%s', skipping pager\n") % command 1347 _(b"missing pager command '%s', skipping pager\n") % command
1343 ) 1348 )
1344 return False 1349 return False
1345 raise 1350 raise
1346 1351
1347 # back up original file descriptors 1352 # back up original file descriptors
1352 if self._isatty(procutil.stderr): 1357 if self._isatty(procutil.stderr):
1353 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno()) 1358 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1354 1359
1355 @self.atexit 1360 @self.atexit
1356 def killpager(): 1361 def killpager():
1357 if util.safehasattr(signal, "SIGINT"): 1362 if util.safehasattr(signal, b"SIGINT"):
1358 signal.signal(signal.SIGINT, signal.SIG_IGN) 1363 signal.signal(signal.SIGINT, signal.SIG_IGN)
1359 # restore original fds, closing pager.stdin copies in the process 1364 # restore original fds, closing pager.stdin copies in the process
1360 os.dup2(stdoutfd, procutil.stdout.fileno()) 1365 os.dup2(stdoutfd, procutil.stdout.fileno())
1361 os.dup2(stderrfd, procutil.stderr.fileno()) 1366 os.dup2(stderrfd, procutil.stderr.fileno())
1362 pager.stdin.close() 1367 pager.stdin.close()
1395 ui.interface.histedit = text 1400 ui.interface.histedit = text
1396 1401
1397 Then histedit will use the text interface and chunkselector will use 1402 Then histedit will use the text interface and chunkselector will use
1398 the default curses interface (crecord at the moment). 1403 the default curses interface (crecord at the moment).
1399 """ 1404 """
1400 alldefaults = frozenset(["text", "curses"]) 1405 alldefaults = frozenset([b"text", b"curses"])
1401 1406
1402 featureinterfaces = { 1407 featureinterfaces = {
1403 "chunkselector": ["text", "curses",], 1408 b"chunkselector": [b"text", b"curses",],
1404 "histedit": ["text", "curses",], 1409 b"histedit": [b"text", b"curses",],
1405 } 1410 }
1406 1411
1407 # Feature-specific interface 1412 # Feature-specific interface
1408 if feature not in featureinterfaces.keys(): 1413 if feature not in featureinterfaces.keys():
1409 # Programming error, not user error 1414 # Programming error, not user error
1410 raise ValueError("Unknown feature requested %s" % feature) 1415 raise ValueError(b"Unknown feature requested %s" % feature)
1411 1416
1412 availableinterfaces = frozenset(featureinterfaces[feature]) 1417 availableinterfaces = frozenset(featureinterfaces[feature])
1413 if alldefaults > availableinterfaces: 1418 if alldefaults > availableinterfaces:
1414 # Programming error, not user error. We need a use case to 1419 # Programming error, not user error. We need a use case to
1415 # define the right thing to do here. 1420 # define the right thing to do here.
1416 raise ValueError( 1421 raise ValueError(
1417 "Feature %s does not handle all default interfaces" % feature 1422 b"Feature %s does not handle all default interfaces" % feature
1418 ) 1423 )
1419 1424
1420 if self.plain() or encoding.environ.get('TERM') == 'dumb': 1425 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1421 return "text" 1426 return b"text"
1422 1427
1423 # Default interface for all the features 1428 # Default interface for all the features
1424 defaultinterface = "text" 1429 defaultinterface = b"text"
1425 i = self.config("ui", "interface") 1430 i = self.config(b"ui", b"interface")
1426 if i in alldefaults: 1431 if i in alldefaults:
1427 defaultinterface = i 1432 defaultinterface = i
1428 1433
1429 choseninterface = defaultinterface 1434 choseninterface = defaultinterface
1430 f = self.config("ui", "interface.%s" % feature) 1435 f = self.config(b"ui", b"interface.%s" % feature)
1431 if f in availableinterfaces: 1436 if f in availableinterfaces:
1432 choseninterface = f 1437 choseninterface = f
1433 1438
1434 if i is not None and defaultinterface != i: 1439 if i is not None and defaultinterface != i:
1435 if f is not None: 1440 if f is not None:
1436 self.warn(_("invalid value for ui.interface: %s\n") % (i,)) 1441 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1437 else: 1442 else:
1438 self.warn( 1443 self.warn(
1439 _("invalid value for ui.interface: %s (using %s)\n") 1444 _(b"invalid value for ui.interface: %s (using %s)\n")
1440 % (i, choseninterface) 1445 % (i, choseninterface)
1441 ) 1446 )
1442 if f is not None and choseninterface != f: 1447 if f is not None and choseninterface != f:
1443 self.warn( 1448 self.warn(
1444 _("invalid value for ui.interface.%s: %s (using %s)\n") 1449 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1445 % (feature, f, choseninterface) 1450 % (feature, f, choseninterface)
1446 ) 1451 )
1447 1452
1448 return choseninterface 1453 return choseninterface
1449 1454
1459 configuration variable or - if it is unset - when `sys.stdin' points 1464 configuration variable or - if it is unset - when `sys.stdin' points
1460 to a terminal device. 1465 to a terminal device.
1461 1466
1462 This function refers to input only; for output, see `ui.formatted()'. 1467 This function refers to input only; for output, see `ui.formatted()'.
1463 ''' 1468 '''
1464 i = self.configbool("ui", "interactive") 1469 i = self.configbool(b"ui", b"interactive")
1465 if i is None: 1470 if i is None:
1466 # some environments replace stdin without implementing isatty 1471 # some environments replace stdin without implementing isatty
1467 # usually those are non-interactive 1472 # usually those are non-interactive
1468 return self._isatty(self._fin) 1473 return self._isatty(self._fin)
1469 1474
1470 return i 1475 return i
1471 1476
1472 def termwidth(self): 1477 def termwidth(self):
1473 '''how wide is the terminal in columns? 1478 '''how wide is the terminal in columns?
1474 ''' 1479 '''
1475 if 'COLUMNS' in encoding.environ: 1480 if b'COLUMNS' in encoding.environ:
1476 try: 1481 try:
1477 return int(encoding.environ['COLUMNS']) 1482 return int(encoding.environ[b'COLUMNS'])
1478 except ValueError: 1483 except ValueError:
1479 pass 1484 pass
1480 return scmutil.termsize(self)[0] 1485 return scmutil.termsize(self)[0]
1481 1486
1482 def formatted(self): 1487 def formatted(self):
1497 This function always returns false when in plain mode, see `ui.plain()'. 1502 This function always returns false when in plain mode, see `ui.plain()'.
1498 ''' 1503 '''
1499 if self.plain(): 1504 if self.plain():
1500 return False 1505 return False
1501 1506
1502 i = self.configbool("ui", "formatted") 1507 i = self.configbool(b"ui", b"formatted")
1503 if i is None: 1508 if i is None:
1504 # some environments replace stdout without implementing isatty 1509 # some environments replace stdout without implementing isatty
1505 # usually those are non-interactive 1510 # usually those are non-interactive
1506 return self._isatty(self._fout) 1511 return self._isatty(self._fout)
1507 1512
1508 return i 1513 return i
1509 1514
1510 def _readline(self, prompt=' ', promptopts=None): 1515 def _readline(self, prompt=b' ', promptopts=None):
1511 # Replacing stdin/stdout temporarily is a hard problem on Python 3 1516 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1512 # because they have to be text streams with *no buffering*. Instead, 1517 # because they have to be text streams with *no buffering*. Instead,
1513 # we use rawinput() only if call_readline() will be invoked by 1518 # we use rawinput() only if call_readline() will be invoked by
1514 # PyOS_Readline(), so no I/O will be made at Python layer. 1519 # PyOS_Readline(), so no I/O will be made at Python layer.
1515 usereadline = ( 1520 usereadline = (
1528 readline.read_history_file 1533 readline.read_history_file
1529 # windows sometimes raises something other than ImportError 1534 # windows sometimes raises something other than ImportError
1530 except Exception: 1535 except Exception:
1531 usereadline = False 1536 usereadline = False
1532 1537
1533 if self._colormode == 'win32' or not usereadline: 1538 if self._colormode == b'win32' or not usereadline:
1534 if not promptopts: 1539 if not promptopts:
1535 promptopts = {} 1540 promptopts = {}
1536 self._writemsgnobuf( 1541 self._writemsgnobuf(
1537 self._fmsgout, prompt, type='prompt', **promptopts 1542 self._fmsgout, prompt, type=b'prompt', **promptopts
1538 ) 1543 )
1539 self.flush() 1544 self.flush()
1540 prompt = ' ' 1545 prompt = b' '
1541 else: 1546 else:
1542 prompt = self.label(prompt, 'ui.prompt') + ' ' 1547 prompt = self.label(prompt, b'ui.prompt') + b' '
1543 1548
1544 # prompt ' ' must exist; otherwise readline may delete entire line 1549 # prompt ' ' must exist; otherwise readline may delete entire line
1545 # - http://bugs.python.org/issue12833 1550 # - http://bugs.python.org/issue12833
1546 with self.timeblockedsection('stdio'): 1551 with self.timeblockedsection(b'stdio'):
1547 if usereadline: 1552 if usereadline:
1548 line = encoding.strtolocal(pycompat.rawinput(prompt)) 1553 line = encoding.strtolocal(pycompat.rawinput(prompt))
1549 # When stdin is in binary mode on Windows, it can cause 1554 # When stdin is in binary mode on Windows, it can cause
1550 # raw_input() to emit an extra trailing carriage return 1555 # raw_input() to emit an extra trailing carriage return
1551 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'): 1556 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1558 raise EOFError 1563 raise EOFError
1559 line = line.rstrip(pycompat.oslinesep) 1564 line = line.rstrip(pycompat.oslinesep)
1560 1565
1561 return line 1566 return line
1562 1567
1563 def prompt(self, msg, default="y"): 1568 def prompt(self, msg, default=b"y"):
1564 """Prompt user with msg, read response. 1569 """Prompt user with msg, read response.
1565 If ui is not interactive, the default is returned. 1570 If ui is not interactive, the default is returned.
1566 """ 1571 """
1567 return self._prompt(msg, default=default) 1572 return self._prompt(msg, default=default)
1568 1573
1569 def _prompt(self, msg, **opts): 1574 def _prompt(self, msg, **opts):
1570 default = opts[r'default'] 1575 default = opts[r'default']
1571 if not self.interactive(): 1576 if not self.interactive():
1572 self._writemsg(self._fmsgout, msg, ' ', type='prompt', **opts) 1577 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1573 self._writemsg( 1578 self._writemsg(
1574 self._fmsgout, default or '', "\n", type='promptecho' 1579 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1575 ) 1580 )
1576 return default 1581 return default
1577 try: 1582 try:
1578 r = self._readline(prompt=msg, promptopts=opts) 1583 r = self._readline(prompt=msg, promptopts=opts)
1579 if not r: 1584 if not r:
1580 r = default 1585 r = default
1581 if self.configbool('ui', 'promptecho'): 1586 if self.configbool(b'ui', b'promptecho'):
1582 self._writemsg(self._fmsgout, r, "\n", type='promptecho') 1587 self._writemsg(self._fmsgout, r, b"\n", type=b'promptecho')
1583 return r 1588 return r
1584 except EOFError: 1589 except EOFError:
1585 raise error.ResponseExpected() 1590 raise error.ResponseExpected()
1586 1591
1587 @staticmethod 1592 @staticmethod
1604 # prompt to start parsing. Sadly, we also can't rely on 1609 # prompt to start parsing. Sadly, we also can't rely on
1605 # choices containing spaces, ASCII, or basically anything 1610 # choices containing spaces, ASCII, or basically anything
1606 # except an ampersand followed by a character. 1611 # except an ampersand followed by a character.
1607 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt) 1612 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1608 msg = m.group(1) 1613 msg = m.group(1)
1609 choices = [p.strip(' ') for p in m.group(2).split('$$')] 1614 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1610 1615
1611 def choicetuple(s): 1616 def choicetuple(s):
1612 ampidx = s.index('&') 1617 ampidx = s.index(b'&')
1613 return s[ampidx + 1 : ampidx + 2].lower(), s.replace('&', '', 1) 1618 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1614 1619
1615 return (msg, [choicetuple(s) for s in choices]) 1620 return (msg, [choicetuple(s) for s in choices])
1616 1621
1617 def promptchoice(self, prompt, default=0): 1622 def promptchoice(self, prompt, default=0):
1618 """Prompt user with a message, read response, and ensure it matches 1623 """Prompt user with a message, read response, and ensure it matches
1630 while True: 1635 while True:
1631 r = self._prompt(msg, default=resps[default], choices=choices) 1636 r = self._prompt(msg, default=resps[default], choices=choices)
1632 if r.lower() in resps: 1637 if r.lower() in resps:
1633 return resps.index(r.lower()) 1638 return resps.index(r.lower())
1634 # TODO: shouldn't it be a warning? 1639 # TODO: shouldn't it be a warning?
1635 self._writemsg(self._fmsgout, _("unrecognized response\n")) 1640 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1636 1641
1637 def getpass(self, prompt=None, default=None): 1642 def getpass(self, prompt=None, default=None):
1638 if not self.interactive(): 1643 if not self.interactive():
1639 return default 1644 return default
1640 try: 1645 try:
1641 self._writemsg( 1646 self._writemsg(
1642 self._fmsgerr, 1647 self._fmsgerr,
1643 prompt or _('password: '), 1648 prompt or _(b'password: '),
1644 type='prompt', 1649 type=b'prompt',
1645 password=True, 1650 password=True,
1646 ) 1651 )
1647 # disable getpass() only if explicitly specified. it's still valid 1652 # disable getpass() only if explicitly specified. it's still valid
1648 # to interact with tty even if fin is not a tty. 1653 # to interact with tty even if fin is not a tty.
1649 with self.timeblockedsection('stdio'): 1654 with self.timeblockedsection(b'stdio'):
1650 if self.configbool('ui', 'nontty'): 1655 if self.configbool(b'ui', b'nontty'):
1651 l = self._fin.readline() 1656 l = self._fin.readline()
1652 if not l: 1657 if not l:
1653 raise EOFError 1658 raise EOFError
1654 return l.rstrip('\n') 1659 return l.rstrip(b'\n')
1655 else: 1660 else:
1656 return getpass.getpass(r'') 1661 return getpass.getpass(r'')
1657 except EOFError: 1662 except EOFError:
1658 raise error.ResponseExpected() 1663 raise error.ResponseExpected()
1659 1664
1661 '''write status message to output (if ui.quiet is False) 1666 '''write status message to output (if ui.quiet is False)
1662 1667
1663 This adds an output label of "ui.status". 1668 This adds an output label of "ui.status".
1664 ''' 1669 '''
1665 if not self.quiet: 1670 if not self.quiet:
1666 self._writemsg(self._fmsgout, type='status', *msg, **opts) 1671 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1667 1672
1668 def warn(self, *msg, **opts): 1673 def warn(self, *msg, **opts):
1669 '''write warning message to output (stderr) 1674 '''write warning message to output (stderr)
1670 1675
1671 This adds an output label of "ui.warning". 1676 This adds an output label of "ui.warning".
1672 ''' 1677 '''
1673 self._writemsg(self._fmsgerr, type='warning', *msg, **opts) 1678 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1674 1679
1675 def error(self, *msg, **opts): 1680 def error(self, *msg, **opts):
1676 '''write error message to output (stderr) 1681 '''write error message to output (stderr)
1677 1682
1678 This adds an output label of "ui.error". 1683 This adds an output label of "ui.error".
1679 ''' 1684 '''
1680 self._writemsg(self._fmsgerr, type='error', *msg, **opts) 1685 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1681 1686
1682 def note(self, *msg, **opts): 1687 def note(self, *msg, **opts):
1683 '''write note to output (if ui.verbose is True) 1688 '''write note to output (if ui.verbose is True)
1684 1689
1685 This adds an output label of "ui.note". 1690 This adds an output label of "ui.note".
1686 ''' 1691 '''
1687 if self.verbose: 1692 if self.verbose:
1688 self._writemsg(self._fmsgout, type='note', *msg, **opts) 1693 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1689 1694
1690 def debug(self, *msg, **opts): 1695 def debug(self, *msg, **opts):
1691 '''write debug message to output (if ui.debugflag is True) 1696 '''write debug message to output (if ui.debugflag is True)
1692 1697
1693 This adds an output label of "ui.debug". 1698 This adds an output label of "ui.debug".
1694 ''' 1699 '''
1695 if self.debugflag: 1700 if self.debugflag:
1696 self._writemsg(self._fmsgout, type='debug', *msg, **opts) 1701 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1697 self.log(b'debug', b'%s', b''.join(msg)) 1702 self.log(b'debug', b'%s', b''.join(msg))
1698 1703
1699 def edit( 1704 def edit(
1700 self, 1705 self,
1701 text, 1706 text,
1706 repopath=None, 1711 repopath=None,
1707 action=None, 1712 action=None,
1708 ): 1713 ):
1709 if action is None: 1714 if action is None:
1710 self.develwarn( 1715 self.develwarn(
1711 'action is None but will soon be a required ' 1716 b'action is None but will soon be a required '
1712 'parameter to ui.edit()' 1717 b'parameter to ui.edit()'
1713 ) 1718 )
1714 extra_defaults = { 1719 extra_defaults = {
1715 'prefix': 'editor', 1720 b'prefix': b'editor',
1716 'suffix': '.txt', 1721 b'suffix': b'.txt',
1717 } 1722 }
1718 if extra is not None: 1723 if extra is not None:
1719 if extra.get('suffix') is not None: 1724 if extra.get(b'suffix') is not None:
1720 self.develwarn( 1725 self.develwarn(
1721 'extra.suffix is not None but will soon be ' 1726 b'extra.suffix is not None but will soon be '
1722 'ignored by ui.edit()' 1727 b'ignored by ui.edit()'
1723 ) 1728 )
1724 extra_defaults.update(extra) 1729 extra_defaults.update(extra)
1725 extra = extra_defaults 1730 extra = extra_defaults
1726 1731
1727 if action == 'diff': 1732 if action == b'diff':
1728 suffix = '.diff' 1733 suffix = b'.diff'
1729 elif action: 1734 elif action:
1730 suffix = '.%s.hg.txt' % action 1735 suffix = b'.%s.hg.txt' % action
1731 else: 1736 else:
1732 suffix = extra['suffix'] 1737 suffix = extra[b'suffix']
1733 1738
1734 rdir = None 1739 rdir = None
1735 if self.configbool('experimental', 'editortmpinhg'): 1740 if self.configbool(b'experimental', b'editortmpinhg'):
1736 rdir = repopath 1741 rdir = repopath
1737 (fd, name) = pycompat.mkstemp( 1742 (fd, name) = pycompat.mkstemp(
1738 prefix='hg-' + extra['prefix'] + '-', suffix=suffix, dir=rdir 1743 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1739 ) 1744 )
1740 try: 1745 try:
1741 f = os.fdopen(fd, r'wb') 1746 f = os.fdopen(fd, r'wb')
1742 f.write(util.tonativeeol(text)) 1747 f.write(util.tonativeeol(text))
1743 f.close() 1748 f.close()
1744 1749
1745 environ = {'HGUSER': user} 1750 environ = {b'HGUSER': user}
1746 if 'transplant_source' in extra: 1751 if b'transplant_source' in extra:
1747 environ.update({'HGREVISION': hex(extra['transplant_source'])}) 1752 environ.update(
1748 for label in ('intermediate-source', 'source', 'rebase_source'): 1753 {b'HGREVISION': hex(extra[b'transplant_source'])}
1754 )
1755 for label in (b'intermediate-source', b'source', b'rebase_source'):
1749 if label in extra: 1756 if label in extra:
1750 environ.update({'HGREVISION': extra[label]}) 1757 environ.update({b'HGREVISION': extra[label]})
1751 break 1758 break
1752 if editform: 1759 if editform:
1753 environ.update({'HGEDITFORM': editform}) 1760 environ.update({b'HGEDITFORM': editform})
1754 if pending: 1761 if pending:
1755 environ.update({'HG_PENDING': pending}) 1762 environ.update({b'HG_PENDING': pending})
1756 1763
1757 editor = self.geteditor() 1764 editor = self.geteditor()
1758 1765
1759 self.system( 1766 self.system(
1760 "%s \"%s\"" % (editor, name), 1767 b"%s \"%s\"" % (editor, name),
1761 environ=environ, 1768 environ=environ,
1762 onerr=error.Abort, 1769 onerr=error.Abort,
1763 errprefix=_("edit failed"), 1770 errprefix=_(b"edit failed"),
1764 blockedtag='editor', 1771 blockedtag=b'editor',
1765 ) 1772 )
1766 1773
1767 f = open(name, r'rb') 1774 f = open(name, r'rb')
1768 t = util.fromnativeeol(f.read()) 1775 t = util.fromnativeeol(f.read())
1769 f.close() 1776 f.close()
1789 ''' 1796 '''
1790 if blockedtag is None: 1797 if blockedtag is None:
1791 # Long cmds tend to be because of an absolute path on cmd. Keep 1798 # Long cmds tend to be because of an absolute path on cmd. Keep
1792 # the tail end instead 1799 # the tail end instead
1793 cmdsuffix = cmd.translate(None, _keepalnum)[-85:] 1800 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1794 blockedtag = 'unknown_system_' + cmdsuffix 1801 blockedtag = b'unknown_system_' + cmdsuffix
1795 out = self._fout 1802 out = self._fout
1796 if any(s[1] for s in self._bufferstates): 1803 if any(s[1] for s in self._bufferstates):
1797 out = self 1804 out = self
1798 with self.timeblockedsection(blockedtag): 1805 with self.timeblockedsection(blockedtag):
1799 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out) 1806 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1800 if rc and onerr: 1807 if rc and onerr:
1801 errmsg = '%s %s' % ( 1808 errmsg = b'%s %s' % (
1802 os.path.basename(cmd.split(None, 1)[0]), 1809 os.path.basename(cmd.split(None, 1)[0]),
1803 procutil.explainexit(rc), 1810 procutil.explainexit(rc),
1804 ) 1811 )
1805 if errprefix: 1812 if errprefix:
1806 errmsg = '%s: %s' % (errprefix, errmsg) 1813 errmsg = b'%s: %s' % (errprefix, errmsg)
1807 raise onerr(errmsg) 1814 raise onerr(errmsg)
1808 return rc 1815 return rc
1809 1816
1810 def _runsystem(self, cmd, environ, cwd, out): 1817 def _runsystem(self, cmd, environ, cwd, out):
1811 """actually execute the given shell command (can be overridden by 1818 """actually execute the given shell command (can be overridden by
1826 exctb = traceback.format_tb(exc[2]) 1833 exctb = traceback.format_tb(exc[2])
1827 exconly = traceback.format_exception_only(cause[0], cause[1]) 1834 exconly = traceback.format_exception_only(cause[0], cause[1])
1828 1835
1829 # exclude frame where 'exc' was chained and rethrown from exctb 1836 # exclude frame where 'exc' was chained and rethrown from exctb
1830 self.write_err( 1837 self.write_err(
1831 'Traceback (most recent call last):\n', 1838 b'Traceback (most recent call last):\n',
1832 ''.join(exctb[:-1]), 1839 b''.join(exctb[:-1]),
1833 ''.join(causetb), 1840 b''.join(causetb),
1834 ''.join(exconly), 1841 b''.join(exconly),
1835 ) 1842 )
1836 else: 1843 else:
1837 output = traceback.format_exception(exc[0], exc[1], exc[2]) 1844 output = traceback.format_exception(exc[0], exc[1], exc[2])
1838 self.write_err(encoding.strtolocal(r''.join(output))) 1845 self.write_err(encoding.strtolocal(r''.join(output)))
1839 return self.tracebackflag or force 1846 return self.tracebackflag or force
1840 1847
1841 def geteditor(self): 1848 def geteditor(self):
1842 '''return editor to use''' 1849 '''return editor to use'''
1843 if pycompat.sysplatform == 'plan9': 1850 if pycompat.sysplatform == b'plan9':
1844 # vi is the MIPS instruction simulator on Plan 9. We 1851 # vi is the MIPS instruction simulator on Plan 9. We
1845 # instead default to E to plumb commit messages to 1852 # instead default to E to plumb commit messages to
1846 # avoid confusion. 1853 # avoid confusion.
1847 editor = 'E' 1854 editor = b'E'
1848 else: 1855 else:
1849 editor = 'vi' 1856 editor = b'vi'
1850 return encoding.environ.get("HGEDITOR") or self.config( 1857 return encoding.environ.get(b"HGEDITOR") or self.config(
1851 "ui", "editor", editor 1858 b"ui", b"editor", editor
1852 ) 1859 )
1853 1860
1854 @util.propertycache 1861 @util.propertycache
1855 def _progbar(self): 1862 def _progbar(self):
1856 """setup the progbar singleton to the ui object""" 1863 """setup the progbar singleton to the ui object"""
1857 if ( 1864 if (
1858 self.quiet 1865 self.quiet
1859 or self.debugflag 1866 or self.debugflag
1860 or self.configbool('progress', 'disable') 1867 or self.configbool(b'progress', b'disable')
1861 or not progress.shouldprint(self) 1868 or not progress.shouldprint(self)
1862 ): 1869 ):
1863 return None 1870 return None
1864 return getprogbar(self) 1871 return getprogbar(self)
1865 1872
1868 if not haveprogbar(): # nothing loaded yet 1875 if not haveprogbar(): # nothing loaded yet
1869 return 1876 return
1870 if self._progbar is not None and self._progbar.printed: 1877 if self._progbar is not None and self._progbar.printed:
1871 self._progbar.clear() 1878 self._progbar.clear()
1872 1879
1873 def progress(self, topic, pos, item="", unit="", total=None): 1880 def progress(self, topic, pos, item=b"", unit=b"", total=None):
1874 '''show a progress message 1881 '''show a progress message
1875 1882
1876 By default a textual progress bar will be displayed if an operation 1883 By default a textual progress bar will be displayed if an operation
1877 takes too long. 'topic' is the current operation, 'item' is a 1884 takes too long. 'topic' is the current operation, 'item' is a
1878 non-numeric marker of the current position (i.e. the currently 1885 non-numeric marker of the current position (i.e. the currently
1883 Multiple nested topics may be active at a time. 1890 Multiple nested topics may be active at a time.
1884 1891
1885 All topics should be marked closed by setting pos to None at 1892 All topics should be marked closed by setting pos to None at
1886 termination. 1893 termination.
1887 ''' 1894 '''
1888 self.deprecwarn("use ui.makeprogress() instead of ui.progress()", "5.1") 1895 self.deprecwarn(
1896 b"use ui.makeprogress() instead of ui.progress()", b"5.1"
1897 )
1889 progress = self.makeprogress(topic, unit, total) 1898 progress = self.makeprogress(topic, unit, total)
1890 if pos is not None: 1899 if pos is not None:
1891 progress.update(pos, item=item) 1900 progress.update(pos, item=item)
1892 else: 1901 else:
1893 progress.complete() 1902 progress.complete()
1894 1903
1895 def makeprogress(self, topic, unit="", total=None): 1904 def makeprogress(self, topic, unit=b"", total=None):
1896 """Create a progress helper for the specified topic""" 1905 """Create a progress helper for the specified topic"""
1897 if getattr(self._fmsgerr, 'structured', False): 1906 if getattr(self._fmsgerr, 'structured', False):
1898 # channel for machine-readable output with metadata, just send 1907 # channel for machine-readable output with metadata, just send
1899 # raw information 1908 # raw information
1900 # TODO: consider porting some useful information (e.g. estimated 1909 # TODO: consider porting some useful information (e.g. estimated
1979 """issue a developer warning message 1988 """issue a developer warning message
1980 1989
1981 Use 'stacklevel' to report the offender some layers further up in the 1990 Use 'stacklevel' to report the offender some layers further up in the
1982 stack. 1991 stack.
1983 """ 1992 """
1984 if not self.configbool('devel', 'all-warnings'): 1993 if not self.configbool(b'devel', b'all-warnings'):
1985 if config is None or not self.configbool('devel', config): 1994 if config is None or not self.configbool(b'devel', config):
1986 return 1995 return
1987 msg = 'devel-warn: ' + msg 1996 msg = b'devel-warn: ' + msg
1988 stacklevel += 1 # get in develwarn 1997 stacklevel += 1 # get in develwarn
1989 if self.tracebackflag: 1998 if self.tracebackflag:
1990 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout) 1999 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
1991 self.log( 2000 self.log(
1992 'develwarn', 2001 b'develwarn',
1993 '%s at:\n%s' % (msg, ''.join(util.getstackframes(stacklevel))), 2002 b'%s at:\n%s'
2003 % (msg, b''.join(util.getstackframes(stacklevel))),
1994 ) 2004 )
1995 else: 2005 else:
1996 curframe = inspect.currentframe() 2006 curframe = inspect.currentframe()
1997 calframe = inspect.getouterframes(curframe, 2) 2007 calframe = inspect.getouterframes(curframe, 2)
1998 fname, lineno, fmsg = calframe[stacklevel][1:4] 2008 fname, lineno, fmsg = calframe[stacklevel][1:4]
1999 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg) 2009 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2000 self.write_err('%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg)) 2010 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2001 self.log( 2011 self.log(
2002 'develwarn', '%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg 2012 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2003 ) 2013 )
2004 curframe = calframe = None # avoid cycles 2014 curframe = calframe = None # avoid cycles
2005 2015
2006 def deprecwarn(self, msg, version, stacklevel=2): 2016 def deprecwarn(self, msg, version, stacklevel=2):
2007 """issue a deprecation warning 2017 """issue a deprecation warning
2008 2018
2009 - msg: message explaining what is deprecated and how to upgrade, 2019 - msg: message explaining what is deprecated and how to upgrade,
2010 - version: last version where the API will be supported, 2020 - version: last version where the API will be supported,
2011 """ 2021 """
2012 if not ( 2022 if not (
2013 self.configbool('devel', 'all-warnings') 2023 self.configbool(b'devel', b'all-warnings')
2014 or self.configbool('devel', 'deprec-warn') 2024 or self.configbool(b'devel', b'deprec-warn')
2015 ): 2025 ):
2016 return 2026 return
2017 msg += ( 2027 msg += (
2018 "\n(compatibility will be dropped after Mercurial-%s," 2028 b"\n(compatibility will be dropped after Mercurial-%s,"
2019 " update your code.)" 2029 b" update your code.)"
2020 ) % version 2030 ) % version
2021 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn') 2031 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2022 2032
2023 def exportableenviron(self): 2033 def exportableenviron(self):
2024 """The environment variables that are safe to export, e.g. through 2034 """The environment variables that are safe to export, e.g. through
2025 hgweb. 2035 hgweb.
2026 """ 2036 """
2027 return self._exportableenviron 2037 return self._exportableenviron
2028 2038
2029 @contextlib.contextmanager 2039 @contextlib.contextmanager
2030 def configoverride(self, overrides, source=""): 2040 def configoverride(self, overrides, source=b""):
2031 """Context manager for temporary config overrides 2041 """Context manager for temporary config overrides
2032 `overrides` must be a dict of the following structure: 2042 `overrides` must be a dict of the following structure:
2033 {(section, name) : value}""" 2043 {(section, name) : value}"""
2034 backups = {} 2044 backups = {}
2035 try: 2045 try:
2040 finally: 2050 finally:
2041 for __, backup in backups.items(): 2051 for __, backup in backups.items():
2042 self.restoreconfig(backup) 2052 self.restoreconfig(backup)
2043 # just restoring ui.quiet config to the previous value is not enough 2053 # just restoring ui.quiet config to the previous value is not enough
2044 # as it does not update ui.quiet class member 2054 # as it does not update ui.quiet class member
2045 if ('ui', 'quiet') in overrides: 2055 if (b'ui', b'quiet') in overrides:
2046 self.fixconfig(section='ui') 2056 self.fixconfig(section=b'ui')
2047 2057
2048 2058
2049 class paths(dict): 2059 class paths(dict):
2050 """Represents a collection of paths and their configs. 2060 """Represents a collection of paths and their configs.
2051 2061
2054 """ 2064 """
2055 2065
2056 def __init__(self, ui): 2066 def __init__(self, ui):
2057 dict.__init__(self) 2067 dict.__init__(self)
2058 2068
2059 for name, loc in ui.configitems('paths', ignoresub=True): 2069 for name, loc in ui.configitems(b'paths', ignoresub=True):
2060 # No location is the same as not existing. 2070 # No location is the same as not existing.
2061 if not loc: 2071 if not loc:
2062 continue 2072 continue
2063 loc, sub = ui.configsuboptions('paths', name) 2073 loc, sub = ui.configsuboptions(b'paths', name)
2064 self[name] = path(ui, name, rawloc=loc, suboptions=sub) 2074 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
2065 2075
2066 def getpath(self, name, default=None): 2076 def getpath(self, name, default=None):
2067 """Return a ``path`` from a string, falling back to default. 2077 """Return a ``path`` from a string, falling back to default.
2068 2078
2096 # Try to resolve as a local path or URI. 2106 # Try to resolve as a local path or URI.
2097 try: 2107 try:
2098 # We don't pass sub-options in, so no need to pass ui instance. 2108 # We don't pass sub-options in, so no need to pass ui instance.
2099 return path(None, None, rawloc=name) 2109 return path(None, None, rawloc=name)
2100 except ValueError: 2110 except ValueError:
2101 raise error.RepoError(_('repository %s does not exist') % name) 2111 raise error.RepoError(_(b'repository %s does not exist') % name)
2102 2112
2103 2113
2104 _pathsuboptions = {} 2114 _pathsuboptions = {}
2105 2115
2106 2116
2124 return func 2134 return func
2125 2135
2126 return register 2136 return register
2127 2137
2128 2138
2129 @pathsuboption('pushurl', 'pushloc') 2139 @pathsuboption(b'pushurl', b'pushloc')
2130 def pushurlpathoption(ui, path, value): 2140 def pushurlpathoption(ui, path, value):
2131 u = util.url(value) 2141 u = util.url(value)
2132 # Actually require a URL. 2142 # Actually require a URL.
2133 if not u.scheme: 2143 if not u.scheme:
2134 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name) 2144 ui.warn(_(b'(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
2135 return None 2145 return None
2136 2146
2137 # Don't support the #foo syntax in the push URL to declare branch to 2147 # Don't support the #foo syntax in the push URL to declare branch to
2138 # push. 2148 # push.
2139 if u.fragment: 2149 if u.fragment:
2140 ui.warn( 2150 ui.warn(
2141 _('("#fragment" in paths.%s:pushurl not supported; ' 'ignoring)\n') 2151 _(
2152 b'("#fragment" in paths.%s:pushurl not supported; '
2153 b'ignoring)\n'
2154 )
2142 % path.name 2155 % path.name
2143 ) 2156 )
2144 u.fragment = None 2157 u.fragment = None
2145 2158
2146 return bytes(u) 2159 return bytes(u)
2147 2160
2148 2161
2149 @pathsuboption('pushrev', 'pushrev') 2162 @pathsuboption(b'pushrev', b'pushrev')
2150 def pushrevpathoption(ui, path, value): 2163 def pushrevpathoption(ui, path, value):
2151 return value 2164 return value
2152 2165
2153 2166
2154 class path(object): 2167 class path(object):
2165 If ``name`` is not defined, we require that the location be a) a local 2178 If ``name`` is not defined, we require that the location be a) a local
2166 filesystem path with a .hg directory or b) a URL. If not, 2179 filesystem path with a .hg directory or b) a URL. If not,
2167 ``ValueError`` is raised. 2180 ``ValueError`` is raised.
2168 """ 2181 """
2169 if not rawloc: 2182 if not rawloc:
2170 raise ValueError('rawloc must be defined') 2183 raise ValueError(b'rawloc must be defined')
2171 2184
2172 # Locations may define branches via syntax <base>#<branch>. 2185 # Locations may define branches via syntax <base>#<branch>.
2173 u = util.url(rawloc) 2186 u = util.url(rawloc)
2174 branch = None 2187 branch = None
2175 if u.fragment: 2188 if u.fragment:
2179 self.url = u 2192 self.url = u
2180 self.branch = branch 2193 self.branch = branch
2181 2194
2182 self.name = name 2195 self.name = name
2183 self.rawloc = rawloc 2196 self.rawloc = rawloc
2184 self.loc = '%s' % u 2197 self.loc = b'%s' % u
2185 2198
2186 # When given a raw location but not a symbolic name, validate the 2199 # When given a raw location but not a symbolic name, validate the
2187 # location is valid. 2200 # location is valid.
2188 if not name and not u.scheme and not self._isvalidlocalpath(self.loc): 2201 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
2189 raise ValueError( 2202 raise ValueError(
2190 'location is not a URL or path to a local ' 'repo: %s' % rawloc 2203 b'location is not a URL or path to a local '
2204 b'repo: %s' % rawloc
2191 ) 2205 )
2192 2206
2193 suboptions = suboptions or {} 2207 suboptions = suboptions or {}
2194 2208
2195 # Now process the sub-options. If a sub-option is registered, its 2209 # Now process the sub-options. If a sub-option is registered, its
2207 """Returns True if the given path is a potentially valid repository. 2221 """Returns True if the given path is a potentially valid repository.
2208 This is its own function so that extensions can change the definition of 2222 This is its own function so that extensions can change the definition of
2209 'valid' in this case (like when pulling from a git repo into a hg 2223 'valid' in this case (like when pulling from a git repo into a hg
2210 one).""" 2224 one)."""
2211 try: 2225 try:
2212 return os.path.isdir(os.path.join(path, '.hg')) 2226 return os.path.isdir(os.path.join(path, b'.hg'))
2213 # Python 2 may return TypeError. Python 3, ValueError. 2227 # Python 2 may return TypeError. Python 3, ValueError.
2214 except (TypeError, ValueError): 2228 except (TypeError, ValueError):
2215 return False 2229 return False
2216 2230
2217 @property 2231 @property
2268 The specified message type is translated to 'ui.<type>' label if the dest 2282 The specified message type is translated to 'ui.<type>' label if the dest
2269 isn't a structured channel, so that the message will be colorized. 2283 isn't a structured channel, so that the message will be colorized.
2270 """ 2284 """
2271 # TODO: maybe change 'type' to a mandatory option 2285 # TODO: maybe change 'type' to a mandatory option
2272 if r'type' in opts and not getattr(dest, 'structured', False): 2286 if r'type' in opts and not getattr(dest, 'structured', False):
2273 opts[r'label'] = opts.get(r'label', '') + ' ui.%s' % opts.pop(r'type') 2287 opts[r'label'] = opts.get(r'label', b'') + b' ui.%s' % opts.pop(r'type')
2274 write(dest, *args, **opts) 2288 write(dest, *args, **opts)