Mercurial > hg
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) |