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): |
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: |
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') |
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 |
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 |
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.""" |
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): |
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 |
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 |
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 |
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: |