##// END OF EJS Templates
py3: fix type of default username
Yuya Nishihara -
r36802:e3732c3a default
parent child Browse files
Show More
@@ -1,394 +1,395 b''
1 1 test-abort-checkin.t
2 2 test-add.t
3 3 test-addremove-similar.t
4 4 test-addremove.t
5 5 test-amend-subrepo.t
6 6 test-ancestor.py
7 7 test-annotate.py
8 8 test-annotate.t
9 9 test-archive-symlinks.t
10 10 test-atomictempfile.py
11 11 test-audit-path.t
12 12 test-audit-subrepo.t
13 13 test-automv.t
14 14 test-backout.t
15 15 test-backwards-remove.t
16 16 test-basic.t
17 17 test-bheads.t
18 18 test-bisect2.t
19 19 test-blackbox.t
20 20 test-bookmarks-current.t
21 21 test-bookmarks-merge.t
22 22 test-bookmarks-rebase.t
23 23 test-bookmarks-strip.t
24 24 test-bookmarks.t
25 25 test-branch-option.t
26 26 test-branch-tag-confict.t
27 27 test-branches.t
28 28 test-bundle-phases.t
29 29 test-bundle-type.t
30 30 test-bundle-vs-outgoing.t
31 31 test-bundle2-multiple-changegroups.t
32 32 test-cappedreader.py
33 33 test-casecollision.t
34 34 test-cat.t
35 35 test-censor.t
36 36 test-changelog-exec.t
37 37 test-check-commit.t
38 38 test-check-execute.t
39 39 test-check-module-imports.t
40 40 test-check-pyflakes.t
41 41 test-check-pylint.t
42 42 test-check-shbang.t
43 43 test-children.t
44 44 test-clone-pull-corruption.t
45 45 test-clone-r.t
46 46 test-clone-update-order.t
47 47 test-command-template.t
48 48 test-commit-amend.t
49 49 test-commit-unresolved.t
50 50 test-commit.t
51 test-committer.t
51 52 test-completion.t
52 53 test-config-env.py
53 54 test-config.t
54 55 test-conflict.t
55 56 test-confused-revert.t
56 57 test-contrib-check-code.t
57 58 test-contrib-check-commit.t
58 59 test-convert-authormap.t
59 60 test-convert-clonebranches.t
60 61 test-convert-datesort.t
61 62 test-convert-filemap.t
62 63 test-convert-hg-sink.t
63 64 test-convert-hg-source.t
64 65 test-convert-hg-startrev.t
65 66 test-copy-move-merge.t
66 67 test-copy.t
67 68 test-copytrace-heuristics.t
68 69 test-debugbuilddag.t
69 70 test-debugbundle.t
70 71 test-debugextensions.t
71 72 test-debugindexdot.t
72 73 test-debugrename.t
73 74 test-default-push.t
74 75 test-diff-binary-file.t
75 76 test-diff-change.t
76 77 test-diff-copy-depth.t
77 78 test-diff-hashes.t
78 79 test-diff-issue2761.t
79 80 test-diff-newlines.t
80 81 test-diff-reverse.t
81 82 test-diff-subdir.t
82 83 test-diffdir.t
83 84 test-directaccess.t
84 85 test-dirstate-backup.t
85 86 test-dirstate-nonnormalset.t
86 87 test-doctest.py
87 88 test-double-merge.t
88 89 test-drawdag.t
89 90 test-duplicateoptions.py
90 91 test-empty-dir.t
91 92 test-empty-file.t
92 93 test-empty-group.t
93 94 test-empty.t
94 95 test-encode.t
95 96 test-encoding-func.py
96 97 test-encoding.t
97 98 test-eol-add.t
98 99 test-eol-clone.t
99 100 test-eol-hook.t
100 101 test-eol-tag.t
101 102 test-eol-update.t
102 103 test-excessive-merge.t
103 104 test-exchange-obsmarkers-case-A1.t
104 105 test-exchange-obsmarkers-case-A2.t
105 106 test-exchange-obsmarkers-case-A3.t
106 107 test-exchange-obsmarkers-case-A4.t
107 108 test-exchange-obsmarkers-case-A5.t
108 109 test-exchange-obsmarkers-case-A6.t
109 110 test-exchange-obsmarkers-case-A7.t
110 111 test-exchange-obsmarkers-case-B1.t
111 112 test-exchange-obsmarkers-case-B2.t
112 113 test-exchange-obsmarkers-case-B3.t
113 114 test-exchange-obsmarkers-case-B4.t
114 115 test-exchange-obsmarkers-case-B5.t
115 116 test-exchange-obsmarkers-case-B6.t
116 117 test-exchange-obsmarkers-case-B7.t
117 118 test-exchange-obsmarkers-case-C1.t
118 119 test-exchange-obsmarkers-case-C2.t
119 120 test-exchange-obsmarkers-case-C3.t
120 121 test-exchange-obsmarkers-case-C4.t
121 122 test-exchange-obsmarkers-case-D1.t
122 123 test-exchange-obsmarkers-case-D2.t
123 124 test-exchange-obsmarkers-case-D3.t
124 125 test-exchange-obsmarkers-case-D4.t
125 126 test-execute-bit.t
126 127 test-extdiff.t
127 128 test-extra-filelog-entry.t
128 129 test-filebranch.t
129 130 test-fileset-generated.t
130 131 test-flags.t
131 132 test-generaldelta.t
132 133 test-getbundle.t
133 134 test-git-export.t
134 135 test-glog-topological.t
135 136 test-gpg.t
136 137 test-graft.t
137 138 test-hghave.t
138 139 test-hgignore.t
139 140 test-hgk.t
140 141 test-hgweb-bundle.t
141 142 test-hgweb-descend-empties.t
142 143 test-hgweb-removed.t
143 144 test-histedit-arguments.t
144 145 test-histedit-base.t
145 146 test-histedit-bookmark-motion.t
146 147 test-histedit-commute.t
147 148 test-histedit-drop.t
148 149 test-histedit-edit.t
149 150 test-histedit-fold-non-commute.t
150 151 test-histedit-fold.t
151 152 test-histedit-no-change.t
152 153 test-histedit-non-commute-abort.t
153 154 test-histedit-non-commute.t
154 155 test-histedit-obsolete.t
155 156 test-histedit-outgoing.t
156 157 test-histedit-templates.t
157 158 test-http-branchmap.t
158 159 test-http-bundle1.t
159 160 test-http-clone-r.t
160 161 test-identify.t
161 162 test-import-unknown.t
162 163 test-imports-checker.t
163 164 test-inherit-mode.t
164 165 test-issue1089.t
165 166 test-issue1102.t
166 167 test-issue1175.t
167 168 test-issue1306.t
168 169 test-issue1438.t
169 170 test-issue1502.t
170 171 test-issue1802.t
171 172 test-issue1877.t
172 173 test-issue1993.t
173 174 test-issue2137.t
174 175 test-issue3084.t
175 176 test-issue4074.t
176 177 test-issue522.t
177 178 test-issue586.t
178 179 test-issue612.t
179 180 test-issue619.t
180 181 test-issue672.t
181 182 test-issue842.t
182 183 test-journal-exists.t
183 184 test-largefiles-cache.t
184 185 test-largefiles-misc.t
185 186 test-largefiles-small-disk.t
186 187 test-locate.t
187 188 test-lock-badness.t
188 189 test-log.t
189 190 test-logexchange.t
190 191 test-lrucachedict.py
191 192 test-mactext.t
192 193 test-manifest-merging.t
193 194 test-manifest.py
194 195 test-manifest.t
195 196 test-match.py
196 197 test-mdiff.py
197 198 test-merge-closedheads.t
198 199 test-merge-commit.t
199 200 test-merge-criss-cross.t
200 201 test-merge-default.t
201 202 test-merge-internal-tools-pattern.t
202 203 test-merge-local.t
203 204 test-merge-remove.t
204 205 test-merge-revert.t
205 206 test-merge-revert2.t
206 207 test-merge-subrepos.t
207 208 test-merge-symlinks.t
208 209 test-merge-types.t
209 210 test-merge1.t
210 211 test-merge10.t
211 212 test-merge2.t
212 213 test-merge4.t
213 214 test-merge5.t
214 215 test-merge6.t
215 216 test-merge7.t
216 217 test-merge8.t
217 218 test-merge9.t
218 219 test-mq-git.t
219 220 test-mq-header-date.t
220 221 test-mq-header-from.t
221 222 test-mq-pull-from-bundle.t
222 223 test-mq-qdiff.t
223 224 test-mq-qfold.t
224 225 test-mq-qgoto.t
225 226 test-mq-qimport-fail-cleanup.t
226 227 test-mq-qpush-exact.t
227 228 test-mq-qqueue.t
228 229 test-mq-qrefresh-replace-log-message.t
229 230 test-mq-qrefresh.t
230 231 test-mq-qrename.t
231 232 test-mq-qsave.t
232 233 test-mq-safety.t
233 234 test-mq-symlinks.t
234 235 test-mv-cp-st-diff.t
235 236 test-narrow-archive.t
236 237 test-narrow-clone-no-ellipsis.t
237 238 test-narrow-clone-nonlinear.t
238 239 test-narrow-clone.t
239 240 test-narrow-commit.t
240 241 test-narrow-copies.t
241 242 test-narrow-debugcommands.t
242 243 test-narrow-debugrebuilddirstate.t
243 244 test-narrow-exchange-merges.t
244 245 test-narrow-exchange.t
245 246 test-narrow-expanddirstate.t
246 247 test-narrow-merge.t
247 248 test-narrow-patch.t
248 249 test-narrow-patterns.t
249 250 test-narrow-pull.t
250 251 test-narrow-rebase.t
251 252 test-narrow-shallow-merges.t
252 253 test-narrow-shallow.t
253 254 test-narrow-strip.t
254 255 test-narrow-update.t
255 256 test-nested-repo.t
256 257 test-newbranch.t
257 258 test-obshistory.t
258 259 test-obsmarker-template.t
259 260 test-obsmarkers-effectflag.t
260 261 test-obsolete-bundle-strip.t
261 262 test-obsolete-changeset-exchange.t
262 263 test-obsolete-checkheads.t
263 264 test-obsolete-distributed.t
264 265 test-obsolete-tag-cache.t
265 266 test-parents.t
266 267 test-pathconflicts-merge.t
267 268 test-pathconflicts-update.t
268 269 test-pending.t
269 270 test-permissions.t
270 271 test-phases.t
271 272 test-pull-branch.t
272 273 test-pull-http.t
273 274 test-pull-permission.t
274 275 test-pull-pull-corruption.t
275 276 test-pull-r.t
276 277 test-pull-update.t
277 278 test-purge.t
278 279 test-push-checkheads-partial-C1.t
279 280 test-push-checkheads-partial-C2.t
280 281 test-push-checkheads-partial-C3.t
281 282 test-push-checkheads-partial-C4.t
282 283 test-push-checkheads-pruned-B1.t
283 284 test-push-checkheads-pruned-B2.t
284 285 test-push-checkheads-pruned-B3.t
285 286 test-push-checkheads-pruned-B4.t
286 287 test-push-checkheads-pruned-B5.t
287 288 test-push-checkheads-pruned-B6.t
288 289 test-push-checkheads-pruned-B7.t
289 290 test-push-checkheads-pruned-B8.t
290 291 test-push-checkheads-superceed-A1.t
291 292 test-push-checkheads-superceed-A2.t
292 293 test-push-checkheads-superceed-A3.t
293 294 test-push-checkheads-superceed-A4.t
294 295 test-push-checkheads-superceed-A5.t
295 296 test-push-checkheads-superceed-A6.t
296 297 test-push-checkheads-superceed-A7.t
297 298 test-push-checkheads-superceed-A8.t
298 299 test-push-checkheads-unpushed-D1.t
299 300 test-push-checkheads-unpushed-D2.t
300 301 test-push-checkheads-unpushed-D3.t
301 302 test-push-checkheads-unpushed-D4.t
302 303 test-push-checkheads-unpushed-D5.t
303 304 test-push-checkheads-unpushed-D6.t
304 305 test-push-checkheads-unpushed-D7.t
305 306 test-push-http.t
306 307 test-push-warn.t
307 308 test-pushvars.t
308 309 test-rebase-abort.t
309 310 test-rebase-base-flag.t
310 311 test-rebase-bookmarks.t
311 312 test-rebase-brute-force.t
312 313 test-rebase-cache.t
313 314 test-rebase-check-restore.t
314 315 test-rebase-collapse.t
315 316 test-rebase-dest.t
316 317 test-rebase-detach.t
317 318 test-rebase-emptycommit.t
318 319 test-rebase-inmemory.t
319 320 test-rebase-interruptions.t
320 321 test-rebase-issue-noparam-single-rev.t
321 322 test-rebase-legacy.t
322 323 test-rebase-mq-skip.t
323 324 test-rebase-named-branches.t
324 325 test-rebase-newancestor.t
325 326 test-rebase-obsolete.t
326 327 test-rebase-parameters.t
327 328 test-rebase-partial.t
328 329 test-rebase-pull.t
329 330 test-rebase-rename.t
330 331 test-rebase-scenario-global.t
331 332 test-rebase-templates.t
332 333 test-rebase-transaction.t
333 334 test-record.t
334 335 test-relink.t
335 336 test-remove.t
336 337 test-rename-after-merge.t
337 338 test-rename-dir-merge.t
338 339 test-rename-merge1.t
339 340 test-rename.t
340 341 test-repair-strip.t
341 342 test-repo-compengines.t
342 343 test-resolve.t
343 344 test-revert-flags.t
344 345 test-revert-unknown.t
345 346 test-revlog-ancestry.py
346 347 test-revlog-group-emptyiter.t
347 348 test-revlog-mmapindex.t
348 349 test-revlog-packentry.t
349 350 test-revset-dirstate-parents.t
350 351 test-revset-outgoing.t
351 352 test-rollback.t
352 353 test-run-tests.py
353 354 test-schemes.t
354 355 test-serve.t
355 356 test-share.t
356 357 test-show-stack.t
357 358 test-show-work.t
358 359 test-show.t
359 360 test-simple-update.t
360 361 test-single-head.t
361 362 test-sparse-clear.t
362 363 test-sparse-merges.t
363 364 test-sparse-requirement.t
364 365 test-sparse-verbose-json.t
365 366 test-ssh-clone-r.t
366 367 test-ssh-proto.t
367 368 test-sshserver.py
368 369 test-status-rev.t
369 370 test-status-terse.t
370 371 test-strip-cross.t
371 372 test-strip.t
372 373 test-subrepo-deep-nested-change.t
373 374 test-subrepo.t
374 375 test-symlinks.t
375 376 test-tag.t
376 377 test-tags.t
377 378 test-treemanifest.t
378 379 test-unamend.t
379 380 test-uncommit.t
380 381 test-unified-test.t
381 382 test-unrelated-pull.t
382 383 test-up-local-change.t
383 384 test-update-branches.t
384 385 test-update-dest.t
385 386 test-update-issue1456.t
386 387 test-update-names.t
387 388 test-update-reverse.t
388 389 test-upgrade-repo.t
389 390 test-url-rev.t
390 391 test-username-newline.t
391 392 test-verify.t
392 393 test-websub.t
393 394 test-win32text.t
394 395 test-xdg.t
@@ -1,1850 +1,1851 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11 import contextlib
12 12 import errno
13 13 import getpass
14 14 import inspect
15 15 import os
16 16 import re
17 17 import signal
18 18 import socket
19 19 import subprocess
20 20 import sys
21 21 import tempfile
22 22 import traceback
23 23
24 24 from .i18n import _
25 25 from .node import hex
26 26
27 27 from . import (
28 28 color,
29 29 config,
30 30 configitems,
31 31 encoding,
32 32 error,
33 33 formatter,
34 34 progress,
35 35 pycompat,
36 36 rcutil,
37 37 scmutil,
38 38 util,
39 39 )
40 40 from .utils import dateutil
41 41
42 42 urlreq = util.urlreq
43 43
44 44 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
45 45 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
46 46 if not c.isalnum())
47 47
48 48 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
49 49 tweakrc = b"""
50 50 [ui]
51 51 # The rollback command is dangerous. As a rule, don't use it.
52 52 rollback = False
53 53 # Make `hg status` report copy information
54 54 statuscopies = yes
55 55 # Prefer curses UIs when available. Revert to plain-text with `text`.
56 56 interface = curses
57 57
58 58 [commands]
59 59 # Make `hg status` emit cwd-relative paths by default.
60 60 status.relative = yes
61 61 # Refuse to perform an `hg update` that would cause a file content merge
62 62 update.check = noconflict
63 63
64 64 [diff]
65 65 git = 1
66 66 showfunc = 1
67 67 """
68 68
69 69 samplehgrcs = {
70 70 'user':
71 71 b"""# example user config (see 'hg help config' for more info)
72 72 [ui]
73 73 # name and email, e.g.
74 74 # username = Jane Doe <jdoe@example.com>
75 75 username =
76 76
77 77 # We recommend enabling tweakdefaults to get slight improvements to
78 78 # the UI over time. Make sure to set HGPLAIN in the environment when
79 79 # writing scripts!
80 80 # tweakdefaults = True
81 81
82 82 # uncomment to disable color in command output
83 83 # (see 'hg help color' for details)
84 84 # color = never
85 85
86 86 # uncomment to disable command output pagination
87 87 # (see 'hg help pager' for details)
88 88 # paginate = never
89 89
90 90 [extensions]
91 91 # uncomment these lines to enable some popular extensions
92 92 # (see 'hg help extensions' for more info)
93 93 #
94 94 # churn =
95 95 """,
96 96
97 97 'cloned':
98 98 b"""# example repository config (see 'hg help config' for more info)
99 99 [paths]
100 100 default = %s
101 101
102 102 # path aliases to other clones of this repo in URLs or filesystem paths
103 103 # (see 'hg help config.paths' for more info)
104 104 #
105 105 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
106 106 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
107 107 # my-clone = /home/jdoe/jdoes-clone
108 108
109 109 [ui]
110 110 # name and email (local to this repository, optional), e.g.
111 111 # username = Jane Doe <jdoe@example.com>
112 112 """,
113 113
114 114 'local':
115 115 b"""# example repository config (see 'hg help config' for more info)
116 116 [paths]
117 117 # path aliases to other clones of this repo in URLs or filesystem paths
118 118 # (see 'hg help config.paths' for more info)
119 119 #
120 120 # default = http://example.com/hg/example-repo
121 121 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
122 122 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
123 123 # my-clone = /home/jdoe/jdoes-clone
124 124
125 125 [ui]
126 126 # name and email (local to this repository, optional), e.g.
127 127 # username = Jane Doe <jdoe@example.com>
128 128 """,
129 129
130 130 'global':
131 131 b"""# example system-wide hg config (see 'hg help config' for more info)
132 132
133 133 [ui]
134 134 # uncomment to disable color in command output
135 135 # (see 'hg help color' for details)
136 136 # color = never
137 137
138 138 # uncomment to disable command output pagination
139 139 # (see 'hg help pager' for details)
140 140 # paginate = never
141 141
142 142 [extensions]
143 143 # uncomment these lines to enable some popular extensions
144 144 # (see 'hg help extensions' for more info)
145 145 #
146 146 # blackbox =
147 147 # churn =
148 148 """,
149 149 }
150 150
151 151 def _maybestrurl(maybebytes):
152 152 return util.rapply(pycompat.strurl, maybebytes)
153 153
154 154 def _maybebytesurl(maybestr):
155 155 return util.rapply(pycompat.bytesurl, maybestr)
156 156
157 157 class httppasswordmgrdbproxy(object):
158 158 """Delays loading urllib2 until it's needed."""
159 159 def __init__(self):
160 160 self._mgr = None
161 161
162 162 def _get_mgr(self):
163 163 if self._mgr is None:
164 164 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
165 165 return self._mgr
166 166
167 167 def add_password(self, realm, uris, user, passwd):
168 168 return self._get_mgr().add_password(
169 169 _maybestrurl(realm), _maybestrurl(uris),
170 170 _maybestrurl(user), _maybestrurl(passwd))
171 171
172 172 def find_user_password(self, realm, uri):
173 173 mgr = self._get_mgr()
174 174 return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
175 175 _maybestrurl(uri)))
176 176
177 177 def _catchterm(*args):
178 178 raise error.SignalInterrupt
179 179
180 180 # unique object used to detect no default value has been provided when
181 181 # retrieving configuration value.
182 182 _unset = object()
183 183
184 184 # _reqexithandlers: callbacks run at the end of a request
185 185 _reqexithandlers = []
186 186
187 187 class ui(object):
188 188 def __init__(self, src=None):
189 189 """Create a fresh new ui object if no src given
190 190
191 191 Use uimod.ui.load() to create a ui which knows global and user configs.
192 192 In most cases, you should use ui.copy() to create a copy of an existing
193 193 ui object.
194 194 """
195 195 # _buffers: used for temporary capture of output
196 196 self._buffers = []
197 197 # 3-tuple describing how each buffer in the stack behaves.
198 198 # Values are (capture stderr, capture subprocesses, apply labels).
199 199 self._bufferstates = []
200 200 # When a buffer is active, defines whether we are expanding labels.
201 201 # This exists to prevent an extra list lookup.
202 202 self._bufferapplylabels = None
203 203 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
204 204 self._reportuntrusted = True
205 205 self._knownconfig = configitems.coreitems
206 206 self._ocfg = config.config() # overlay
207 207 self._tcfg = config.config() # trusted
208 208 self._ucfg = config.config() # untrusted
209 209 self._trustusers = set()
210 210 self._trustgroups = set()
211 211 self.callhooks = True
212 212 # Insecure server connections requested.
213 213 self.insecureconnections = False
214 214 # Blocked time
215 215 self.logblockedtimes = False
216 216 # color mode: see mercurial/color.py for possible value
217 217 self._colormode = None
218 218 self._terminfoparams = {}
219 219 self._styles = {}
220 220
221 221 if src:
222 222 self.fout = src.fout
223 223 self.ferr = src.ferr
224 224 self.fin = src.fin
225 225 self.pageractive = src.pageractive
226 226 self._disablepager = src._disablepager
227 227 self._tweaked = src._tweaked
228 228
229 229 self._tcfg = src._tcfg.copy()
230 230 self._ucfg = src._ucfg.copy()
231 231 self._ocfg = src._ocfg.copy()
232 232 self._trustusers = src._trustusers.copy()
233 233 self._trustgroups = src._trustgroups.copy()
234 234 self.environ = src.environ
235 235 self.callhooks = src.callhooks
236 236 self.insecureconnections = src.insecureconnections
237 237 self._colormode = src._colormode
238 238 self._terminfoparams = src._terminfoparams.copy()
239 239 self._styles = src._styles.copy()
240 240
241 241 self.fixconfig()
242 242
243 243 self.httppasswordmgrdb = src.httppasswordmgrdb
244 244 self._blockedtimes = src._blockedtimes
245 245 else:
246 246 self.fout = util.stdout
247 247 self.ferr = util.stderr
248 248 self.fin = util.stdin
249 249 self.pageractive = False
250 250 self._disablepager = False
251 251 self._tweaked = False
252 252
253 253 # shared read-only environment
254 254 self.environ = encoding.environ
255 255
256 256 self.httppasswordmgrdb = httppasswordmgrdbproxy()
257 257 self._blockedtimes = collections.defaultdict(int)
258 258
259 259 allowed = self.configlist('experimental', 'exportableenviron')
260 260 if '*' in allowed:
261 261 self._exportableenviron = self.environ
262 262 else:
263 263 self._exportableenviron = {}
264 264 for k in allowed:
265 265 if k in self.environ:
266 266 self._exportableenviron[k] = self.environ[k]
267 267
268 268 @classmethod
269 269 def load(cls):
270 270 """Create a ui and load global and user configs"""
271 271 u = cls()
272 272 # we always trust global config files and environment variables
273 273 for t, f in rcutil.rccomponents():
274 274 if t == 'path':
275 275 u.readconfig(f, trust=True)
276 276 elif t == 'items':
277 277 sections = set()
278 278 for section, name, value, source in f:
279 279 # do not set u._ocfg
280 280 # XXX clean this up once immutable config object is a thing
281 281 u._tcfg.set(section, name, value, source)
282 282 u._ucfg.set(section, name, value, source)
283 283 sections.add(section)
284 284 for section in sections:
285 285 u.fixconfig(section=section)
286 286 else:
287 287 raise error.ProgrammingError('unknown rctype: %s' % t)
288 288 u._maybetweakdefaults()
289 289 return u
290 290
291 291 def _maybetweakdefaults(self):
292 292 if not self.configbool('ui', 'tweakdefaults'):
293 293 return
294 294 if self._tweaked or self.plain('tweakdefaults'):
295 295 return
296 296
297 297 # Note: it is SUPER IMPORTANT that you set self._tweaked to
298 298 # True *before* any calls to setconfig(), otherwise you'll get
299 299 # infinite recursion between setconfig and this method.
300 300 #
301 301 # TODO: We should extract an inner method in setconfig() to
302 302 # avoid this weirdness.
303 303 self._tweaked = True
304 304 tmpcfg = config.config()
305 305 tmpcfg.parse('<tweakdefaults>', tweakrc)
306 306 for section in tmpcfg:
307 307 for name, value in tmpcfg.items(section):
308 308 if not self.hasconfig(section, name):
309 309 self.setconfig(section, name, value, "<tweakdefaults>")
310 310
311 311 def copy(self):
312 312 return self.__class__(self)
313 313
314 314 def resetstate(self):
315 315 """Clear internal state that shouldn't persist across commands"""
316 316 if self._progbar:
317 317 self._progbar.resetstate() # reset last-print time of progress bar
318 318 self.httppasswordmgrdb = httppasswordmgrdbproxy()
319 319
320 320 @contextlib.contextmanager
321 321 def timeblockedsection(self, key):
322 322 # this is open-coded below - search for timeblockedsection to find them
323 323 starttime = util.timer()
324 324 try:
325 325 yield
326 326 finally:
327 327 self._blockedtimes[key + '_blocked'] += \
328 328 (util.timer() - starttime) * 1000
329 329
330 330 def formatter(self, topic, opts):
331 331 return formatter.formatter(self, self, topic, opts)
332 332
333 333 def _trusted(self, fp, f):
334 334 st = util.fstat(fp)
335 335 if util.isowner(st):
336 336 return True
337 337
338 338 tusers, tgroups = self._trustusers, self._trustgroups
339 339 if '*' in tusers or '*' in tgroups:
340 340 return True
341 341
342 342 user = util.username(st.st_uid)
343 343 group = util.groupname(st.st_gid)
344 344 if user in tusers or group in tgroups or user == util.username():
345 345 return True
346 346
347 347 if self._reportuntrusted:
348 348 self.warn(_('not trusting file %s from untrusted '
349 349 'user %s, group %s\n') % (f, user, group))
350 350 return False
351 351
352 352 def readconfig(self, filename, root=None, trust=False,
353 353 sections=None, remap=None):
354 354 try:
355 355 fp = open(filename, u'rb')
356 356 except IOError:
357 357 if not sections: # ignore unless we were looking for something
358 358 return
359 359 raise
360 360
361 361 cfg = config.config()
362 362 trusted = sections or trust or self._trusted(fp, filename)
363 363
364 364 try:
365 365 cfg.read(filename, fp, sections=sections, remap=remap)
366 366 fp.close()
367 367 except error.ConfigError as inst:
368 368 if trusted:
369 369 raise
370 370 self.warn(_("ignored: %s\n") % util.forcebytestr(inst))
371 371
372 372 if self.plain():
373 373 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
374 374 'logtemplate', 'statuscopies', 'style',
375 375 'traceback', 'verbose'):
376 376 if k in cfg['ui']:
377 377 del cfg['ui'][k]
378 378 for k, v in cfg.items('defaults'):
379 379 del cfg['defaults'][k]
380 380 for k, v in cfg.items('commands'):
381 381 del cfg['commands'][k]
382 382 # Don't remove aliases from the configuration if in the exceptionlist
383 383 if self.plain('alias'):
384 384 for k, v in cfg.items('alias'):
385 385 del cfg['alias'][k]
386 386 if self.plain('revsetalias'):
387 387 for k, v in cfg.items('revsetalias'):
388 388 del cfg['revsetalias'][k]
389 389 if self.plain('templatealias'):
390 390 for k, v in cfg.items('templatealias'):
391 391 del cfg['templatealias'][k]
392 392
393 393 if trusted:
394 394 self._tcfg.update(cfg)
395 395 self._tcfg.update(self._ocfg)
396 396 self._ucfg.update(cfg)
397 397 self._ucfg.update(self._ocfg)
398 398
399 399 if root is None:
400 400 root = os.path.expanduser('~')
401 401 self.fixconfig(root=root)
402 402
403 403 def fixconfig(self, root=None, section=None):
404 404 if section in (None, 'paths'):
405 405 # expand vars and ~
406 406 # translate paths relative to root (or home) into absolute paths
407 407 root = root or pycompat.getcwd()
408 408 for c in self._tcfg, self._ucfg, self._ocfg:
409 409 for n, p in c.items('paths'):
410 410 # Ignore sub-options.
411 411 if ':' in n:
412 412 continue
413 413 if not p:
414 414 continue
415 415 if '%%' in p:
416 416 s = self.configsource('paths', n) or 'none'
417 417 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
418 418 % (n, p, s))
419 419 p = p.replace('%%', '%')
420 420 p = util.expandpath(p)
421 421 if not util.hasscheme(p) and not os.path.isabs(p):
422 422 p = os.path.normpath(os.path.join(root, p))
423 423 c.set("paths", n, p)
424 424
425 425 if section in (None, 'ui'):
426 426 # update ui options
427 427 self.debugflag = self.configbool('ui', 'debug')
428 428 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
429 429 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
430 430 if self.verbose and self.quiet:
431 431 self.quiet = self.verbose = False
432 432 self._reportuntrusted = self.debugflag or self.configbool("ui",
433 433 "report_untrusted")
434 434 self.tracebackflag = self.configbool('ui', 'traceback')
435 435 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
436 436
437 437 if section in (None, 'trusted'):
438 438 # update trust information
439 439 self._trustusers.update(self.configlist('trusted', 'users'))
440 440 self._trustgroups.update(self.configlist('trusted', 'groups'))
441 441
442 442 def backupconfig(self, section, item):
443 443 return (self._ocfg.backup(section, item),
444 444 self._tcfg.backup(section, item),
445 445 self._ucfg.backup(section, item),)
446 446 def restoreconfig(self, data):
447 447 self._ocfg.restore(data[0])
448 448 self._tcfg.restore(data[1])
449 449 self._ucfg.restore(data[2])
450 450
451 451 def setconfig(self, section, name, value, source=''):
452 452 for cfg in (self._ocfg, self._tcfg, self._ucfg):
453 453 cfg.set(section, name, value, source)
454 454 self.fixconfig(section=section)
455 455 self._maybetweakdefaults()
456 456
457 457 def _data(self, untrusted):
458 458 return untrusted and self._ucfg or self._tcfg
459 459
460 460 def configsource(self, section, name, untrusted=False):
461 461 return self._data(untrusted).source(section, name)
462 462
463 463 def config(self, section, name, default=_unset, untrusted=False):
464 464 """return the plain string version of a config"""
465 465 value = self._config(section, name, default=default,
466 466 untrusted=untrusted)
467 467 if value is _unset:
468 468 return None
469 469 return value
470 470
471 471 def _config(self, section, name, default=_unset, untrusted=False):
472 472 value = itemdefault = default
473 473 item = self._knownconfig.get(section, {}).get(name)
474 474 alternates = [(section, name)]
475 475
476 476 if item is not None:
477 477 alternates.extend(item.alias)
478 478 if callable(item.default):
479 479 itemdefault = item.default()
480 480 else:
481 481 itemdefault = item.default
482 482 else:
483 483 msg = ("accessing unregistered config item: '%s.%s'")
484 484 msg %= (section, name)
485 485 self.develwarn(msg, 2, 'warn-config-unknown')
486 486
487 487 if default is _unset:
488 488 if item is None:
489 489 value = default
490 490 elif item.default is configitems.dynamicdefault:
491 491 value = None
492 492 msg = "config item requires an explicit default value: '%s.%s'"
493 493 msg %= (section, name)
494 494 self.develwarn(msg, 2, 'warn-config-default')
495 495 else:
496 496 value = itemdefault
497 497 elif (item is not None
498 498 and item.default is not configitems.dynamicdefault
499 499 and default != itemdefault):
500 500 msg = ("specifying a mismatched default value for a registered "
501 501 "config item: '%s.%s' '%s'")
502 502 msg %= (section, name, pycompat.bytestr(default))
503 503 self.develwarn(msg, 2, 'warn-config-default')
504 504
505 505 for s, n in alternates:
506 506 candidate = self._data(untrusted).get(s, n, None)
507 507 if candidate is not None:
508 508 value = candidate
509 509 section = s
510 510 name = n
511 511 break
512 512
513 513 if self.debugflag and not untrusted and self._reportuntrusted:
514 514 for s, n in alternates:
515 515 uvalue = self._ucfg.get(s, n)
516 516 if uvalue is not None and uvalue != value:
517 517 self.debug("ignoring untrusted configuration option "
518 518 "%s.%s = %s\n" % (s, n, uvalue))
519 519 return value
520 520
521 521 def configsuboptions(self, section, name, default=_unset, untrusted=False):
522 522 """Get a config option and all sub-options.
523 523
524 524 Some config options have sub-options that are declared with the
525 525 format "key:opt = value". This method is used to return the main
526 526 option and all its declared sub-options.
527 527
528 528 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
529 529 is a dict of defined sub-options where keys and values are strings.
530 530 """
531 531 main = self.config(section, name, default, untrusted=untrusted)
532 532 data = self._data(untrusted)
533 533 sub = {}
534 534 prefix = '%s:' % name
535 535 for k, v in data.items(section):
536 536 if k.startswith(prefix):
537 537 sub[k[len(prefix):]] = v
538 538
539 539 if self.debugflag and not untrusted and self._reportuntrusted:
540 540 for k, v in sub.items():
541 541 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
542 542 if uvalue is not None and uvalue != v:
543 543 self.debug('ignoring untrusted configuration option '
544 544 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
545 545
546 546 return main, sub
547 547
548 548 def configpath(self, section, name, default=_unset, untrusted=False):
549 549 'get a path config item, expanded relative to repo root or config file'
550 550 v = self.config(section, name, default, untrusted)
551 551 if v is None:
552 552 return None
553 553 if not os.path.isabs(v) or "://" not in v:
554 554 src = self.configsource(section, name, untrusted)
555 555 if ':' in src:
556 556 base = os.path.dirname(src.rsplit(':')[0])
557 557 v = os.path.join(base, os.path.expanduser(v))
558 558 return v
559 559
560 560 def configbool(self, section, name, default=_unset, untrusted=False):
561 561 """parse a configuration element as a boolean
562 562
563 563 >>> u = ui(); s = b'foo'
564 564 >>> u.setconfig(s, b'true', b'yes')
565 565 >>> u.configbool(s, b'true')
566 566 True
567 567 >>> u.setconfig(s, b'false', b'no')
568 568 >>> u.configbool(s, b'false')
569 569 False
570 570 >>> u.configbool(s, b'unknown')
571 571 False
572 572 >>> u.configbool(s, b'unknown', True)
573 573 True
574 574 >>> u.setconfig(s, b'invalid', b'somevalue')
575 575 >>> u.configbool(s, b'invalid')
576 576 Traceback (most recent call last):
577 577 ...
578 578 ConfigError: foo.invalid is not a boolean ('somevalue')
579 579 """
580 580
581 581 v = self._config(section, name, default, untrusted=untrusted)
582 582 if v is None:
583 583 return v
584 584 if v is _unset:
585 585 if default is _unset:
586 586 return False
587 587 return default
588 588 if isinstance(v, bool):
589 589 return v
590 590 b = util.parsebool(v)
591 591 if b is None:
592 592 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
593 593 % (section, name, v))
594 594 return b
595 595
596 596 def configwith(self, convert, section, name, default=_unset,
597 597 desc=None, untrusted=False):
598 598 """parse a configuration element with a conversion function
599 599
600 600 >>> u = ui(); s = b'foo'
601 601 >>> u.setconfig(s, b'float1', b'42')
602 602 >>> u.configwith(float, s, b'float1')
603 603 42.0
604 604 >>> u.setconfig(s, b'float2', b'-4.25')
605 605 >>> u.configwith(float, s, b'float2')
606 606 -4.25
607 607 >>> u.configwith(float, s, b'unknown', 7)
608 608 7.0
609 609 >>> u.setconfig(s, b'invalid', b'somevalue')
610 610 >>> u.configwith(float, s, b'invalid')
611 611 Traceback (most recent call last):
612 612 ...
613 613 ConfigError: foo.invalid is not a valid float ('somevalue')
614 614 >>> u.configwith(float, s, b'invalid', desc=b'womble')
615 615 Traceback (most recent call last):
616 616 ...
617 617 ConfigError: foo.invalid is not a valid womble ('somevalue')
618 618 """
619 619
620 620 v = self.config(section, name, default, untrusted)
621 621 if v is None:
622 622 return v # do not attempt to convert None
623 623 try:
624 624 return convert(v)
625 625 except (ValueError, error.ParseError):
626 626 if desc is None:
627 627 desc = pycompat.sysbytes(convert.__name__)
628 628 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
629 629 % (section, name, desc, v))
630 630
631 631 def configint(self, section, name, default=_unset, untrusted=False):
632 632 """parse a configuration element as an integer
633 633
634 634 >>> u = ui(); s = b'foo'
635 635 >>> u.setconfig(s, b'int1', b'42')
636 636 >>> u.configint(s, b'int1')
637 637 42
638 638 >>> u.setconfig(s, b'int2', b'-42')
639 639 >>> u.configint(s, b'int2')
640 640 -42
641 641 >>> u.configint(s, b'unknown', 7)
642 642 7
643 643 >>> u.setconfig(s, b'invalid', b'somevalue')
644 644 >>> u.configint(s, b'invalid')
645 645 Traceback (most recent call last):
646 646 ...
647 647 ConfigError: foo.invalid is not a valid integer ('somevalue')
648 648 """
649 649
650 650 return self.configwith(int, section, name, default, 'integer',
651 651 untrusted)
652 652
653 653 def configbytes(self, section, name, default=_unset, untrusted=False):
654 654 """parse a configuration element as a quantity in bytes
655 655
656 656 Units can be specified as b (bytes), k or kb (kilobytes), m or
657 657 mb (megabytes), g or gb (gigabytes).
658 658
659 659 >>> u = ui(); s = b'foo'
660 660 >>> u.setconfig(s, b'val1', b'42')
661 661 >>> u.configbytes(s, b'val1')
662 662 42
663 663 >>> u.setconfig(s, b'val2', b'42.5 kb')
664 664 >>> u.configbytes(s, b'val2')
665 665 43520
666 666 >>> u.configbytes(s, b'unknown', b'7 MB')
667 667 7340032
668 668 >>> u.setconfig(s, b'invalid', b'somevalue')
669 669 >>> u.configbytes(s, b'invalid')
670 670 Traceback (most recent call last):
671 671 ...
672 672 ConfigError: foo.invalid is not a byte quantity ('somevalue')
673 673 """
674 674
675 675 value = self._config(section, name, default, untrusted)
676 676 if value is _unset:
677 677 if default is _unset:
678 678 default = 0
679 679 value = default
680 680 if not isinstance(value, bytes):
681 681 return value
682 682 try:
683 683 return util.sizetoint(value)
684 684 except error.ParseError:
685 685 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
686 686 % (section, name, value))
687 687
688 688 def configlist(self, section, name, default=_unset, untrusted=False):
689 689 """parse a configuration element as a list of comma/space separated
690 690 strings
691 691
692 692 >>> u = ui(); s = b'foo'
693 693 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
694 694 >>> u.configlist(s, b'list1')
695 695 ['this', 'is', 'a small', 'test']
696 696 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
697 697 >>> u.configlist(s, b'list2')
698 698 ['this', 'is', 'a small', 'test']
699 699 """
700 700 # default is not always a list
701 701 v = self.configwith(config.parselist, section, name, default,
702 702 'list', untrusted)
703 703 if isinstance(v, bytes):
704 704 return config.parselist(v)
705 705 elif v is None:
706 706 return []
707 707 return v
708 708
709 709 def configdate(self, section, name, default=_unset, untrusted=False):
710 710 """parse a configuration element as a tuple of ints
711 711
712 712 >>> u = ui(); s = b'foo'
713 713 >>> u.setconfig(s, b'date', b'0 0')
714 714 >>> u.configdate(s, b'date')
715 715 (0, 0)
716 716 """
717 717 if self.config(section, name, default, untrusted):
718 718 return self.configwith(dateutil.parsedate, section, name, default,
719 719 'date', untrusted)
720 720 if default is _unset:
721 721 return None
722 722 return default
723 723
724 724 def hasconfig(self, section, name, untrusted=False):
725 725 return self._data(untrusted).hasitem(section, name)
726 726
727 727 def has_section(self, section, untrusted=False):
728 728 '''tell whether section exists in config.'''
729 729 return section in self._data(untrusted)
730 730
731 731 def configitems(self, section, untrusted=False, ignoresub=False):
732 732 items = self._data(untrusted).items(section)
733 733 if ignoresub:
734 734 newitems = {}
735 735 for k, v in items:
736 736 if ':' not in k:
737 737 newitems[k] = v
738 738 items = list(newitems.iteritems())
739 739 if self.debugflag and not untrusted and self._reportuntrusted:
740 740 for k, v in self._ucfg.items(section):
741 741 if self._tcfg.get(section, k) != v:
742 742 self.debug("ignoring untrusted configuration option "
743 743 "%s.%s = %s\n" % (section, k, v))
744 744 return items
745 745
746 746 def walkconfig(self, untrusted=False):
747 747 cfg = self._data(untrusted)
748 748 for section in cfg.sections():
749 749 for name, value in self.configitems(section, untrusted):
750 750 yield section, name, value
751 751
752 752 def plain(self, feature=None):
753 753 '''is plain mode active?
754 754
755 755 Plain mode means that all configuration variables which affect
756 756 the behavior and output of Mercurial should be
757 757 ignored. Additionally, the output should be stable,
758 758 reproducible and suitable for use in scripts or applications.
759 759
760 760 The only way to trigger plain mode is by setting either the
761 761 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
762 762
763 763 The return value can either be
764 764 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
765 765 - False if feature is disabled by default and not included in HGPLAIN
766 766 - True otherwise
767 767 '''
768 768 if ('HGPLAIN' not in encoding.environ and
769 769 'HGPLAINEXCEPT' not in encoding.environ):
770 770 return False
771 771 exceptions = encoding.environ.get('HGPLAINEXCEPT',
772 772 '').strip().split(',')
773 773 # TODO: add support for HGPLAIN=+feature,-feature syntax
774 774 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
775 775 exceptions.append('strictflags')
776 776 if feature and exceptions:
777 777 return feature not in exceptions
778 778 return True
779 779
780 780 def username(self, acceptempty=False):
781 781 """Return default username to be used in commits.
782 782
783 783 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
784 784 and stop searching if one of these is set.
785 785 If not found and acceptempty is True, returns None.
786 786 If not found and ui.askusername is True, ask the user, else use
787 787 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
788 788 If no username could be found, raise an Abort error.
789 789 """
790 790 user = encoding.environ.get("HGUSER")
791 791 if user is None:
792 792 user = self.config("ui", "username")
793 793 if user is not None:
794 794 user = os.path.expandvars(user)
795 795 if user is None:
796 796 user = encoding.environ.get("EMAIL")
797 797 if user is None and acceptempty:
798 798 return user
799 799 if user is None and self.configbool("ui", "askusername"):
800 800 user = self.prompt(_("enter a commit username:"), default=None)
801 801 if user is None and not self.interactive():
802 802 try:
803 user = '%s@%s' % (util.getuser(), socket.getfqdn())
803 user = '%s@%s' % (util.getuser(),
804 encoding.strtolocal(socket.getfqdn()))
804 805 self.warn(_("no username found, using '%s' instead\n") % user)
805 806 except KeyError:
806 807 pass
807 808 if not user:
808 809 raise error.Abort(_('no username supplied'),
809 810 hint=_("use 'hg config --edit' "
810 811 'to set your username'))
811 812 if "\n" in user:
812 813 raise error.Abort(_("username %r contains a newline\n")
813 814 % pycompat.bytestr(user))
814 815 return user
815 816
816 817 def shortuser(self, user):
817 818 """Return a short representation of a user name or email address."""
818 819 if not self.verbose:
819 820 user = util.shortuser(user)
820 821 return user
821 822
822 823 def expandpath(self, loc, default=None):
823 824 """Return repository location relative to cwd or from [paths]"""
824 825 try:
825 826 p = self.paths.getpath(loc)
826 827 if p:
827 828 return p.rawloc
828 829 except error.RepoError:
829 830 pass
830 831
831 832 if default:
832 833 try:
833 834 p = self.paths.getpath(default)
834 835 if p:
835 836 return p.rawloc
836 837 except error.RepoError:
837 838 pass
838 839
839 840 return loc
840 841
841 842 @util.propertycache
842 843 def paths(self):
843 844 return paths(self)
844 845
845 846 def pushbuffer(self, error=False, subproc=False, labeled=False):
846 847 """install a buffer to capture standard output of the ui object
847 848
848 849 If error is True, the error output will be captured too.
849 850
850 851 If subproc is True, output from subprocesses (typically hooks) will be
851 852 captured too.
852 853
853 854 If labeled is True, any labels associated with buffered
854 855 output will be handled. By default, this has no effect
855 856 on the output returned, but extensions and GUI tools may
856 857 handle this argument and returned styled output. If output
857 858 is being buffered so it can be captured and parsed or
858 859 processed, labeled should not be set to True.
859 860 """
860 861 self._buffers.append([])
861 862 self._bufferstates.append((error, subproc, labeled))
862 863 self._bufferapplylabels = labeled
863 864
864 865 def popbuffer(self):
865 866 '''pop the last buffer and return the buffered output'''
866 867 self._bufferstates.pop()
867 868 if self._bufferstates:
868 869 self._bufferapplylabels = self._bufferstates[-1][2]
869 870 else:
870 871 self._bufferapplylabels = None
871 872
872 873 return "".join(self._buffers.pop())
873 874
874 875 def canwritewithoutlabels(self):
875 876 '''check if write skips the label'''
876 877 if self._buffers and not self._bufferapplylabels:
877 878 return True
878 879 return self._colormode is None
879 880
880 881 def canbatchlabeledwrites(self):
881 882 '''check if write calls with labels are batchable'''
882 883 # Windows color printing is special, see ``write``.
883 884 return self._colormode != 'win32'
884 885
885 886 def write(self, *args, **opts):
886 887 '''write args to output
887 888
888 889 By default, this method simply writes to the buffer or stdout.
889 890 Color mode can be set on the UI class to have the output decorated
890 891 with color modifier before being written to stdout.
891 892
892 893 The color used is controlled by an optional keyword argument, "label".
893 894 This should be a string containing label names separated by space.
894 895 Label names take the form of "topic.type". For example, ui.debug()
895 896 issues a label of "ui.debug".
896 897
897 898 When labeling output for a specific command, a label of
898 899 "cmdname.type" is recommended. For example, status issues
899 900 a label of "status.modified" for modified files.
900 901 '''
901 902 if self._buffers:
902 903 if self._bufferapplylabels:
903 904 label = opts.get(r'label', '')
904 905 self._buffers[-1].extend(self.label(a, label) for a in args)
905 906 else:
906 907 self._buffers[-1].extend(args)
907 908 else:
908 909 self._writenobuf(*args, **opts)
909 910
910 911 def _writenobuf(self, *args, **opts):
911 912 if self._colormode == 'win32':
912 913 # windows color printing is its own can of crab, defer to
913 914 # the color module and that is it.
914 915 color.win32print(self, self._write, *args, **opts)
915 916 else:
916 917 msgs = args
917 918 if self._colormode is not None:
918 919 label = opts.get(r'label', '')
919 920 msgs = [self.label(a, label) for a in args]
920 921 self._write(*msgs, **opts)
921 922
922 923 def _write(self, *msgs, **opts):
923 924 self._progclear()
924 925 # opencode timeblockedsection because this is a critical path
925 926 starttime = util.timer()
926 927 try:
927 928 self.fout.write(''.join(msgs))
928 929 except IOError as err:
929 930 raise error.StdioError(err)
930 931 finally:
931 932 self._blockedtimes['stdio_blocked'] += \
932 933 (util.timer() - starttime) * 1000
933 934
934 935 def write_err(self, *args, **opts):
935 936 self._progclear()
936 937 if self._bufferstates and self._bufferstates[-1][0]:
937 938 self.write(*args, **opts)
938 939 elif self._colormode == 'win32':
939 940 # windows color printing is its own can of crab, defer to
940 941 # the color module and that is it.
941 942 color.win32print(self, self._write_err, *args, **opts)
942 943 else:
943 944 msgs = args
944 945 if self._colormode is not None:
945 946 label = opts.get(r'label', '')
946 947 msgs = [self.label(a, label) for a in args]
947 948 self._write_err(*msgs, **opts)
948 949
949 950 def _write_err(self, *msgs, **opts):
950 951 try:
951 952 with self.timeblockedsection('stdio'):
952 953 if not getattr(self.fout, 'closed', False):
953 954 self.fout.flush()
954 955 for a in msgs:
955 956 self.ferr.write(a)
956 957 # stderr may be buffered under win32 when redirected to files,
957 958 # including stdout.
958 959 if not getattr(self.ferr, 'closed', False):
959 960 self.ferr.flush()
960 961 except IOError as inst:
961 962 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
962 963 raise error.StdioError(inst)
963 964
964 965 def flush(self):
965 966 # opencode timeblockedsection because this is a critical path
966 967 starttime = util.timer()
967 968 try:
968 969 try:
969 970 self.fout.flush()
970 971 except IOError as err:
971 972 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
972 973 raise error.StdioError(err)
973 974 finally:
974 975 try:
975 976 self.ferr.flush()
976 977 except IOError as err:
977 978 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
978 979 raise error.StdioError(err)
979 980 finally:
980 981 self._blockedtimes['stdio_blocked'] += \
981 982 (util.timer() - starttime) * 1000
982 983
983 984 def _isatty(self, fh):
984 985 if self.configbool('ui', 'nontty'):
985 986 return False
986 987 return util.isatty(fh)
987 988
988 989 def disablepager(self):
989 990 self._disablepager = True
990 991
991 992 def pager(self, command):
992 993 """Start a pager for subsequent command output.
993 994
994 995 Commands which produce a long stream of output should call
995 996 this function to activate the user's preferred pagination
996 997 mechanism (which may be no pager). Calling this function
997 998 precludes any future use of interactive functionality, such as
998 999 prompting the user or activating curses.
999 1000
1000 1001 Args:
1001 1002 command: The full, non-aliased name of the command. That is, "log"
1002 1003 not "history, "summary" not "summ", etc.
1003 1004 """
1004 1005 if (self._disablepager
1005 1006 or self.pageractive):
1006 1007 # how pager should do is already determined
1007 1008 return
1008 1009
1009 1010 if not command.startswith('internal-always-') and (
1010 1011 # explicit --pager=on (= 'internal-always-' prefix) should
1011 1012 # take precedence over disabling factors below
1012 1013 command in self.configlist('pager', 'ignore')
1013 1014 or not self.configbool('ui', 'paginate')
1014 1015 or not self.configbool('pager', 'attend-' + command, True)
1015 1016 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1016 1017 # formatted() will need some adjustment.
1017 1018 or not self.formatted()
1018 1019 or self.plain()
1019 1020 or self._buffers
1020 1021 # TODO: expose debugger-enabled on the UI object
1021 1022 or '--debugger' in pycompat.sysargv):
1022 1023 # We only want to paginate if the ui appears to be
1023 1024 # interactive, the user didn't say HGPLAIN or
1024 1025 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1025 1026 return
1026 1027
1027 1028 pagercmd = self.config('pager', 'pager', rcutil.fallbackpager)
1028 1029 if not pagercmd:
1029 1030 return
1030 1031
1031 1032 pagerenv = {}
1032 1033 for name, value in rcutil.defaultpagerenv().items():
1033 1034 if name not in encoding.environ:
1034 1035 pagerenv[name] = value
1035 1036
1036 1037 self.debug('starting pager for command %r\n' % command)
1037 1038 self.flush()
1038 1039
1039 1040 wasformatted = self.formatted()
1040 1041 if util.safehasattr(signal, "SIGPIPE"):
1041 1042 signal.signal(signal.SIGPIPE, _catchterm)
1042 1043 if self._runpager(pagercmd, pagerenv):
1043 1044 self.pageractive = True
1044 1045 # Preserve the formatted-ness of the UI. This is important
1045 1046 # because we mess with stdout, which might confuse
1046 1047 # auto-detection of things being formatted.
1047 1048 self.setconfig('ui', 'formatted', wasformatted, 'pager')
1048 1049 self.setconfig('ui', 'interactive', False, 'pager')
1049 1050
1050 1051 # If pagermode differs from color.mode, reconfigure color now that
1051 1052 # pageractive is set.
1052 1053 cm = self._colormode
1053 1054 if cm != self.config('color', 'pagermode', cm):
1054 1055 color.setup(self)
1055 1056 else:
1056 1057 # If the pager can't be spawned in dispatch when --pager=on is
1057 1058 # given, don't try again when the command runs, to avoid a duplicate
1058 1059 # warning about a missing pager command.
1059 1060 self.disablepager()
1060 1061
1061 1062 def _runpager(self, command, env=None):
1062 1063 """Actually start the pager and set up file descriptors.
1063 1064
1064 1065 This is separate in part so that extensions (like chg) can
1065 1066 override how a pager is invoked.
1066 1067 """
1067 1068 if command == 'cat':
1068 1069 # Save ourselves some work.
1069 1070 return False
1070 1071 # If the command doesn't contain any of these characters, we
1071 1072 # assume it's a binary and exec it directly. This means for
1072 1073 # simple pager command configurations, we can degrade
1073 1074 # gracefully and tell the user about their broken pager.
1074 1075 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
1075 1076
1076 1077 if pycompat.iswindows and not shell:
1077 1078 # Window's built-in `more` cannot be invoked with shell=False, but
1078 1079 # its `more.com` can. Hide this implementation detail from the
1079 1080 # user so we can also get sane bad PAGER behavior. MSYS has
1080 1081 # `more.exe`, so do a cmd.exe style resolution of the executable to
1081 1082 # determine which one to use.
1082 1083 fullcmd = util.findexe(command)
1083 1084 if not fullcmd:
1084 1085 self.warn(_("missing pager command '%s', skipping pager\n")
1085 1086 % command)
1086 1087 return False
1087 1088
1088 1089 command = fullcmd
1089 1090
1090 1091 try:
1091 1092 pager = subprocess.Popen(
1092 1093 command, shell=shell, bufsize=-1,
1093 1094 close_fds=util.closefds, stdin=subprocess.PIPE,
1094 1095 stdout=util.stdout, stderr=util.stderr,
1095 1096 env=util.shellenviron(env))
1096 1097 except OSError as e:
1097 1098 if e.errno == errno.ENOENT and not shell:
1098 1099 self.warn(_("missing pager command '%s', skipping pager\n")
1099 1100 % command)
1100 1101 return False
1101 1102 raise
1102 1103
1103 1104 # back up original file descriptors
1104 1105 stdoutfd = os.dup(util.stdout.fileno())
1105 1106 stderrfd = os.dup(util.stderr.fileno())
1106 1107
1107 1108 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
1108 1109 if self._isatty(util.stderr):
1109 1110 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
1110 1111
1111 1112 @self.atexit
1112 1113 def killpager():
1113 1114 if util.safehasattr(signal, "SIGINT"):
1114 1115 signal.signal(signal.SIGINT, signal.SIG_IGN)
1115 1116 # restore original fds, closing pager.stdin copies in the process
1116 1117 os.dup2(stdoutfd, util.stdout.fileno())
1117 1118 os.dup2(stderrfd, util.stderr.fileno())
1118 1119 pager.stdin.close()
1119 1120 pager.wait()
1120 1121
1121 1122 return True
1122 1123
1123 1124 @property
1124 1125 def _exithandlers(self):
1125 1126 return _reqexithandlers
1126 1127
1127 1128 def atexit(self, func, *args, **kwargs):
1128 1129 '''register a function to run after dispatching a request
1129 1130
1130 1131 Handlers do not stay registered across request boundaries.'''
1131 1132 self._exithandlers.append((func, args, kwargs))
1132 1133 return func
1133 1134
1134 1135 def interface(self, feature):
1135 1136 """what interface to use for interactive console features?
1136 1137
1137 1138 The interface is controlled by the value of `ui.interface` but also by
1138 1139 the value of feature-specific configuration. For example:
1139 1140
1140 1141 ui.interface.histedit = text
1141 1142 ui.interface.chunkselector = curses
1142 1143
1143 1144 Here the features are "histedit" and "chunkselector".
1144 1145
1145 1146 The configuration above means that the default interfaces for commands
1146 1147 is curses, the interface for histedit is text and the interface for
1147 1148 selecting chunk is crecord (the best curses interface available).
1148 1149
1149 1150 Consider the following example:
1150 1151 ui.interface = curses
1151 1152 ui.interface.histedit = text
1152 1153
1153 1154 Then histedit will use the text interface and chunkselector will use
1154 1155 the default curses interface (crecord at the moment).
1155 1156 """
1156 1157 alldefaults = frozenset(["text", "curses"])
1157 1158
1158 1159 featureinterfaces = {
1159 1160 "chunkselector": [
1160 1161 "text",
1161 1162 "curses",
1162 1163 ]
1163 1164 }
1164 1165
1165 1166 # Feature-specific interface
1166 1167 if feature not in featureinterfaces.keys():
1167 1168 # Programming error, not user error
1168 1169 raise ValueError("Unknown feature requested %s" % feature)
1169 1170
1170 1171 availableinterfaces = frozenset(featureinterfaces[feature])
1171 1172 if alldefaults > availableinterfaces:
1172 1173 # Programming error, not user error. We need a use case to
1173 1174 # define the right thing to do here.
1174 1175 raise ValueError(
1175 1176 "Feature %s does not handle all default interfaces" %
1176 1177 feature)
1177 1178
1178 1179 if self.plain():
1179 1180 return "text"
1180 1181
1181 1182 # Default interface for all the features
1182 1183 defaultinterface = "text"
1183 1184 i = self.config("ui", "interface")
1184 1185 if i in alldefaults:
1185 1186 defaultinterface = i
1186 1187
1187 1188 choseninterface = defaultinterface
1188 1189 f = self.config("ui", "interface.%s" % feature)
1189 1190 if f in availableinterfaces:
1190 1191 choseninterface = f
1191 1192
1192 1193 if i is not None and defaultinterface != i:
1193 1194 if f is not None:
1194 1195 self.warn(_("invalid value for ui.interface: %s\n") %
1195 1196 (i,))
1196 1197 else:
1197 1198 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
1198 1199 (i, choseninterface))
1199 1200 if f is not None and choseninterface != f:
1200 1201 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
1201 1202 (feature, f, choseninterface))
1202 1203
1203 1204 return choseninterface
1204 1205
1205 1206 def interactive(self):
1206 1207 '''is interactive input allowed?
1207 1208
1208 1209 An interactive session is a session where input can be reasonably read
1209 1210 from `sys.stdin'. If this function returns false, any attempt to read
1210 1211 from stdin should fail with an error, unless a sensible default has been
1211 1212 specified.
1212 1213
1213 1214 Interactiveness is triggered by the value of the `ui.interactive'
1214 1215 configuration variable or - if it is unset - when `sys.stdin' points
1215 1216 to a terminal device.
1216 1217
1217 1218 This function refers to input only; for output, see `ui.formatted()'.
1218 1219 '''
1219 1220 i = self.configbool("ui", "interactive")
1220 1221 if i is None:
1221 1222 # some environments replace stdin without implementing isatty
1222 1223 # usually those are non-interactive
1223 1224 return self._isatty(self.fin)
1224 1225
1225 1226 return i
1226 1227
1227 1228 def termwidth(self):
1228 1229 '''how wide is the terminal in columns?
1229 1230 '''
1230 1231 if 'COLUMNS' in encoding.environ:
1231 1232 try:
1232 1233 return int(encoding.environ['COLUMNS'])
1233 1234 except ValueError:
1234 1235 pass
1235 1236 return scmutil.termsize(self)[0]
1236 1237
1237 1238 def formatted(self):
1238 1239 '''should formatted output be used?
1239 1240
1240 1241 It is often desirable to format the output to suite the output medium.
1241 1242 Examples of this are truncating long lines or colorizing messages.
1242 1243 However, this is not often not desirable when piping output into other
1243 1244 utilities, e.g. `grep'.
1244 1245
1245 1246 Formatted output is triggered by the value of the `ui.formatted'
1246 1247 configuration variable or - if it is unset - when `sys.stdout' points
1247 1248 to a terminal device. Please note that `ui.formatted' should be
1248 1249 considered an implementation detail; it is not intended for use outside
1249 1250 Mercurial or its extensions.
1250 1251
1251 1252 This function refers to output only; for input, see `ui.interactive()'.
1252 1253 This function always returns false when in plain mode, see `ui.plain()'.
1253 1254 '''
1254 1255 if self.plain():
1255 1256 return False
1256 1257
1257 1258 i = self.configbool("ui", "formatted")
1258 1259 if i is None:
1259 1260 # some environments replace stdout without implementing isatty
1260 1261 # usually those are non-interactive
1261 1262 return self._isatty(self.fout)
1262 1263
1263 1264 return i
1264 1265
1265 1266 def _readline(self):
1266 1267 if self._isatty(self.fin):
1267 1268 try:
1268 1269 # magically add command line editing support, where
1269 1270 # available
1270 1271 import readline
1271 1272 # force demandimport to really load the module
1272 1273 readline.read_history_file
1273 1274 # windows sometimes raises something other than ImportError
1274 1275 except Exception:
1275 1276 pass
1276 1277
1277 1278 # prompt ' ' must exist; otherwise readline may delete entire line
1278 1279 # - http://bugs.python.org/issue12833
1279 1280 with self.timeblockedsection('stdio'):
1280 1281 line = util.bytesinput(self.fin, self.fout, r' ')
1281 1282
1282 1283 # When stdin is in binary mode on Windows, it can cause
1283 1284 # raw_input() to emit an extra trailing carriage return
1284 1285 if pycompat.oslinesep == '\r\n' and line and line[-1] == '\r':
1285 1286 line = line[:-1]
1286 1287 return line
1287 1288
1288 1289 def prompt(self, msg, default="y"):
1289 1290 """Prompt user with msg, read response.
1290 1291 If ui is not interactive, the default is returned.
1291 1292 """
1292 1293 if not self.interactive():
1293 1294 self.write(msg, ' ', default or '', "\n")
1294 1295 return default
1295 1296 self._writenobuf(msg, label='ui.prompt')
1296 1297 self.flush()
1297 1298 try:
1298 1299 r = self._readline()
1299 1300 if not r:
1300 1301 r = default
1301 1302 if self.configbool('ui', 'promptecho'):
1302 1303 self.write(r, "\n")
1303 1304 return r
1304 1305 except EOFError:
1305 1306 raise error.ResponseExpected()
1306 1307
1307 1308 @staticmethod
1308 1309 def extractchoices(prompt):
1309 1310 """Extract prompt message and list of choices from specified prompt.
1310 1311
1311 1312 This returns tuple "(message, choices)", and "choices" is the
1312 1313 list of tuple "(response character, text without &)".
1313 1314
1314 1315 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1315 1316 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1316 1317 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1317 1318 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1318 1319 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1319 1320 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1320 1321 """
1321 1322
1322 1323 # Sadly, the prompt string may have been built with a filename
1323 1324 # containing "$$" so let's try to find the first valid-looking
1324 1325 # prompt to start parsing. Sadly, we also can't rely on
1325 1326 # choices containing spaces, ASCII, or basically anything
1326 1327 # except an ampersand followed by a character.
1327 1328 m = re.match(br'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1328 1329 msg = m.group(1)
1329 1330 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1330 1331 def choicetuple(s):
1331 1332 ampidx = s.index('&')
1332 1333 return s[ampidx + 1:ampidx + 2].lower(), s.replace('&', '', 1)
1333 1334 return (msg, [choicetuple(s) for s in choices])
1334 1335
1335 1336 def promptchoice(self, prompt, default=0):
1336 1337 """Prompt user with a message, read response, and ensure it matches
1337 1338 one of the provided choices. The prompt is formatted as follows:
1338 1339
1339 1340 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1340 1341
1341 1342 The index of the choice is returned. Responses are case
1342 1343 insensitive. If ui is not interactive, the default is
1343 1344 returned.
1344 1345 """
1345 1346
1346 1347 msg, choices = self.extractchoices(prompt)
1347 1348 resps = [r for r, t in choices]
1348 1349 while True:
1349 1350 r = self.prompt(msg, resps[default])
1350 1351 if r.lower() in resps:
1351 1352 return resps.index(r.lower())
1352 1353 self.write(_("unrecognized response\n"))
1353 1354
1354 1355 def getpass(self, prompt=None, default=None):
1355 1356 if not self.interactive():
1356 1357 return default
1357 1358 try:
1358 1359 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1359 1360 # disable getpass() only if explicitly specified. it's still valid
1360 1361 # to interact with tty even if fin is not a tty.
1361 1362 with self.timeblockedsection('stdio'):
1362 1363 if self.configbool('ui', 'nontty'):
1363 1364 l = self.fin.readline()
1364 1365 if not l:
1365 1366 raise EOFError
1366 1367 return l.rstrip('\n')
1367 1368 else:
1368 1369 return getpass.getpass('')
1369 1370 except EOFError:
1370 1371 raise error.ResponseExpected()
1371 1372 def status(self, *msg, **opts):
1372 1373 '''write status message to output (if ui.quiet is False)
1373 1374
1374 1375 This adds an output label of "ui.status".
1375 1376 '''
1376 1377 if not self.quiet:
1377 1378 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1378 1379 self.write(*msg, **opts)
1379 1380 def warn(self, *msg, **opts):
1380 1381 '''write warning message to output (stderr)
1381 1382
1382 1383 This adds an output label of "ui.warning".
1383 1384 '''
1384 1385 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1385 1386 self.write_err(*msg, **opts)
1386 1387 def note(self, *msg, **opts):
1387 1388 '''write note to output (if ui.verbose is True)
1388 1389
1389 1390 This adds an output label of "ui.note".
1390 1391 '''
1391 1392 if self.verbose:
1392 1393 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1393 1394 self.write(*msg, **opts)
1394 1395 def debug(self, *msg, **opts):
1395 1396 '''write debug message to output (if ui.debugflag is True)
1396 1397
1397 1398 This adds an output label of "ui.debug".
1398 1399 '''
1399 1400 if self.debugflag:
1400 1401 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1401 1402 self.write(*msg, **opts)
1402 1403
1403 1404 def edit(self, text, user, extra=None, editform=None, pending=None,
1404 1405 repopath=None, action=None):
1405 1406 if action is None:
1406 1407 self.develwarn('action is None but will soon be a required '
1407 1408 'parameter to ui.edit()')
1408 1409 extra_defaults = {
1409 1410 'prefix': 'editor',
1410 1411 'suffix': '.txt',
1411 1412 }
1412 1413 if extra is not None:
1413 1414 if extra.get('suffix') is not None:
1414 1415 self.develwarn('extra.suffix is not None but will soon be '
1415 1416 'ignored by ui.edit()')
1416 1417 extra_defaults.update(extra)
1417 1418 extra = extra_defaults
1418 1419
1419 1420 if action == 'diff':
1420 1421 suffix = '.diff'
1421 1422 elif action:
1422 1423 suffix = '.%s.hg.txt' % action
1423 1424 else:
1424 1425 suffix = extra['suffix']
1425 1426
1426 1427 rdir = None
1427 1428 if self.configbool('experimental', 'editortmpinhg'):
1428 1429 rdir = repopath
1429 1430 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1430 1431 suffix=suffix,
1431 1432 dir=rdir)
1432 1433 try:
1433 1434 f = os.fdopen(fd, r'wb')
1434 1435 f.write(util.tonativeeol(text))
1435 1436 f.close()
1436 1437
1437 1438 environ = {'HGUSER': user}
1438 1439 if 'transplant_source' in extra:
1439 1440 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1440 1441 for label in ('intermediate-source', 'source', 'rebase_source'):
1441 1442 if label in extra:
1442 1443 environ.update({'HGREVISION': extra[label]})
1443 1444 break
1444 1445 if editform:
1445 1446 environ.update({'HGEDITFORM': editform})
1446 1447 if pending:
1447 1448 environ.update({'HG_PENDING': pending})
1448 1449
1449 1450 editor = self.geteditor()
1450 1451
1451 1452 self.system("%s \"%s\"" % (editor, name),
1452 1453 environ=environ,
1453 1454 onerr=error.Abort, errprefix=_("edit failed"),
1454 1455 blockedtag='editor')
1455 1456
1456 1457 f = open(name, r'rb')
1457 1458 t = util.fromnativeeol(f.read())
1458 1459 f.close()
1459 1460 finally:
1460 1461 os.unlink(name)
1461 1462
1462 1463 return t
1463 1464
1464 1465 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1465 1466 blockedtag=None):
1466 1467 '''execute shell command with appropriate output stream. command
1467 1468 output will be redirected if fout is not stdout.
1468 1469
1469 1470 if command fails and onerr is None, return status, else raise onerr
1470 1471 object as exception.
1471 1472 '''
1472 1473 if blockedtag is None:
1473 1474 # Long cmds tend to be because of an absolute path on cmd. Keep
1474 1475 # the tail end instead
1475 1476 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1476 1477 blockedtag = 'unknown_system_' + cmdsuffix
1477 1478 out = self.fout
1478 1479 if any(s[1] for s in self._bufferstates):
1479 1480 out = self
1480 1481 with self.timeblockedsection(blockedtag):
1481 1482 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1482 1483 if rc and onerr:
1483 1484 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1484 1485 util.explainexit(rc)[0])
1485 1486 if errprefix:
1486 1487 errmsg = '%s: %s' % (errprefix, errmsg)
1487 1488 raise onerr(errmsg)
1488 1489 return rc
1489 1490
1490 1491 def _runsystem(self, cmd, environ, cwd, out):
1491 1492 """actually execute the given shell command (can be overridden by
1492 1493 extensions like chg)"""
1493 1494 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1494 1495
1495 1496 def traceback(self, exc=None, force=False):
1496 1497 '''print exception traceback if traceback printing enabled or forced.
1497 1498 only to call in exception handler. returns true if traceback
1498 1499 printed.'''
1499 1500 if self.tracebackflag or force:
1500 1501 if exc is None:
1501 1502 exc = sys.exc_info()
1502 1503 cause = getattr(exc[1], 'cause', None)
1503 1504
1504 1505 if cause is not None:
1505 1506 causetb = traceback.format_tb(cause[2])
1506 1507 exctb = traceback.format_tb(exc[2])
1507 1508 exconly = traceback.format_exception_only(cause[0], cause[1])
1508 1509
1509 1510 # exclude frame where 'exc' was chained and rethrown from exctb
1510 1511 self.write_err('Traceback (most recent call last):\n',
1511 1512 ''.join(exctb[:-1]),
1512 1513 ''.join(causetb),
1513 1514 ''.join(exconly))
1514 1515 else:
1515 1516 output = traceback.format_exception(exc[0], exc[1], exc[2])
1516 1517 self.write_err(encoding.strtolocal(r''.join(output)))
1517 1518 return self.tracebackflag or force
1518 1519
1519 1520 def geteditor(self):
1520 1521 '''return editor to use'''
1521 1522 if pycompat.sysplatform == 'plan9':
1522 1523 # vi is the MIPS instruction simulator on Plan 9. We
1523 1524 # instead default to E to plumb commit messages to
1524 1525 # avoid confusion.
1525 1526 editor = 'E'
1526 1527 else:
1527 1528 editor = 'vi'
1528 1529 return (encoding.environ.get("HGEDITOR") or
1529 1530 self.config("ui", "editor", editor))
1530 1531
1531 1532 @util.propertycache
1532 1533 def _progbar(self):
1533 1534 """setup the progbar singleton to the ui object"""
1534 1535 if (self.quiet or self.debugflag
1535 1536 or self.configbool('progress', 'disable')
1536 1537 or not progress.shouldprint(self)):
1537 1538 return None
1538 1539 return getprogbar(self)
1539 1540
1540 1541 def _progclear(self):
1541 1542 """clear progress bar output if any. use it before any output"""
1542 1543 if not haveprogbar(): # nothing loaded yet
1543 1544 return
1544 1545 if self._progbar is not None and self._progbar.printed:
1545 1546 self._progbar.clear()
1546 1547
1547 1548 def progress(self, topic, pos, item="", unit="", total=None):
1548 1549 '''show a progress message
1549 1550
1550 1551 By default a textual progress bar will be displayed if an operation
1551 1552 takes too long. 'topic' is the current operation, 'item' is a
1552 1553 non-numeric marker of the current position (i.e. the currently
1553 1554 in-process file), 'pos' is the current numeric position (i.e.
1554 1555 revision, bytes, etc.), unit is a corresponding unit label,
1555 1556 and total is the highest expected pos.
1556 1557
1557 1558 Multiple nested topics may be active at a time.
1558 1559
1559 1560 All topics should be marked closed by setting pos to None at
1560 1561 termination.
1561 1562 '''
1562 1563 if self._progbar is not None:
1563 1564 self._progbar.progress(topic, pos, item=item, unit=unit,
1564 1565 total=total)
1565 1566 if pos is None or not self.configbool('progress', 'debug'):
1566 1567 return
1567 1568
1568 1569 if unit:
1569 1570 unit = ' ' + unit
1570 1571 if item:
1571 1572 item = ' ' + item
1572 1573
1573 1574 if total:
1574 1575 pct = 100.0 * pos / total
1575 1576 self.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1576 1577 % (topic, item, pos, total, unit, pct))
1577 1578 else:
1578 1579 self.debug('%s:%s %d%s\n' % (topic, item, pos, unit))
1579 1580
1580 1581 def log(self, service, *msg, **opts):
1581 1582 '''hook for logging facility extensions
1582 1583
1583 1584 service should be a readily-identifiable subsystem, which will
1584 1585 allow filtering.
1585 1586
1586 1587 *msg should be a newline-terminated format string to log, and
1587 1588 then any values to %-format into that format string.
1588 1589
1589 1590 **opts currently has no defined meanings.
1590 1591 '''
1591 1592
1592 1593 def label(self, msg, label):
1593 1594 '''style msg based on supplied label
1594 1595
1595 1596 If some color mode is enabled, this will add the necessary control
1596 1597 characters to apply such color. In addition, 'debug' color mode adds
1597 1598 markup showing which label affects a piece of text.
1598 1599
1599 1600 ui.write(s, 'label') is equivalent to
1600 1601 ui.write(ui.label(s, 'label')).
1601 1602 '''
1602 1603 if self._colormode is not None:
1603 1604 return color.colorlabel(self, msg, label)
1604 1605 return msg
1605 1606
1606 1607 def develwarn(self, msg, stacklevel=1, config=None):
1607 1608 """issue a developer warning message
1608 1609
1609 1610 Use 'stacklevel' to report the offender some layers further up in the
1610 1611 stack.
1611 1612 """
1612 1613 if not self.configbool('devel', 'all-warnings'):
1613 1614 if config is None or not self.configbool('devel', config):
1614 1615 return
1615 1616 msg = 'devel-warn: ' + msg
1616 1617 stacklevel += 1 # get in develwarn
1617 1618 if self.tracebackflag:
1618 1619 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1619 1620 self.log('develwarn', '%s at:\n%s' %
1620 1621 (msg, ''.join(util.getstackframes(stacklevel))))
1621 1622 else:
1622 1623 curframe = inspect.currentframe()
1623 1624 calframe = inspect.getouterframes(curframe, 2)
1624 1625 fname, lineno, fmsg = calframe[stacklevel][1:4]
1625 1626 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
1626 1627 self.write_err('%s at: %s:%d (%s)\n'
1627 1628 % (msg, fname, lineno, fmsg))
1628 1629 self.log('develwarn', '%s at: %s:%d (%s)\n',
1629 1630 msg, fname, lineno, fmsg)
1630 1631 curframe = calframe = None # avoid cycles
1631 1632
1632 1633 def deprecwarn(self, msg, version, stacklevel=2):
1633 1634 """issue a deprecation warning
1634 1635
1635 1636 - msg: message explaining what is deprecated and how to upgrade,
1636 1637 - version: last version where the API will be supported,
1637 1638 """
1638 1639 if not (self.configbool('devel', 'all-warnings')
1639 1640 or self.configbool('devel', 'deprec-warn')):
1640 1641 return
1641 1642 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1642 1643 " update your code.)") % version
1643 1644 self.develwarn(msg, stacklevel=stacklevel, config='deprec-warn')
1644 1645
1645 1646 def exportableenviron(self):
1646 1647 """The environment variables that are safe to export, e.g. through
1647 1648 hgweb.
1648 1649 """
1649 1650 return self._exportableenviron
1650 1651
1651 1652 @contextlib.contextmanager
1652 1653 def configoverride(self, overrides, source=""):
1653 1654 """Context manager for temporary config overrides
1654 1655 `overrides` must be a dict of the following structure:
1655 1656 {(section, name) : value}"""
1656 1657 backups = {}
1657 1658 try:
1658 1659 for (section, name), value in overrides.items():
1659 1660 backups[(section, name)] = self.backupconfig(section, name)
1660 1661 self.setconfig(section, name, value, source)
1661 1662 yield
1662 1663 finally:
1663 1664 for __, backup in backups.items():
1664 1665 self.restoreconfig(backup)
1665 1666 # just restoring ui.quiet config to the previous value is not enough
1666 1667 # as it does not update ui.quiet class member
1667 1668 if ('ui', 'quiet') in overrides:
1668 1669 self.fixconfig(section='ui')
1669 1670
1670 1671 class paths(dict):
1671 1672 """Represents a collection of paths and their configs.
1672 1673
1673 1674 Data is initially derived from ui instances and the config files they have
1674 1675 loaded.
1675 1676 """
1676 1677 def __init__(self, ui):
1677 1678 dict.__init__(self)
1678 1679
1679 1680 for name, loc in ui.configitems('paths', ignoresub=True):
1680 1681 # No location is the same as not existing.
1681 1682 if not loc:
1682 1683 continue
1683 1684 loc, sub = ui.configsuboptions('paths', name)
1684 1685 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1685 1686
1686 1687 def getpath(self, name, default=None):
1687 1688 """Return a ``path`` from a string, falling back to default.
1688 1689
1689 1690 ``name`` can be a named path or locations. Locations are filesystem
1690 1691 paths or URIs.
1691 1692
1692 1693 Returns None if ``name`` is not a registered path, a URI, or a local
1693 1694 path to a repo.
1694 1695 """
1695 1696 # Only fall back to default if no path was requested.
1696 1697 if name is None:
1697 1698 if not default:
1698 1699 default = ()
1699 1700 elif not isinstance(default, (tuple, list)):
1700 1701 default = (default,)
1701 1702 for k in default:
1702 1703 try:
1703 1704 return self[k]
1704 1705 except KeyError:
1705 1706 continue
1706 1707 return None
1707 1708
1708 1709 # Most likely empty string.
1709 1710 # This may need to raise in the future.
1710 1711 if not name:
1711 1712 return None
1712 1713
1713 1714 try:
1714 1715 return self[name]
1715 1716 except KeyError:
1716 1717 # Try to resolve as a local path or URI.
1717 1718 try:
1718 1719 # We don't pass sub-options in, so no need to pass ui instance.
1719 1720 return path(None, None, rawloc=name)
1720 1721 except ValueError:
1721 1722 raise error.RepoError(_('repository %s does not exist') %
1722 1723 name)
1723 1724
1724 1725 _pathsuboptions = {}
1725 1726
1726 1727 def pathsuboption(option, attr):
1727 1728 """Decorator used to declare a path sub-option.
1728 1729
1729 1730 Arguments are the sub-option name and the attribute it should set on
1730 1731 ``path`` instances.
1731 1732
1732 1733 The decorated function will receive as arguments a ``ui`` instance,
1733 1734 ``path`` instance, and the string value of this option from the config.
1734 1735 The function should return the value that will be set on the ``path``
1735 1736 instance.
1736 1737
1737 1738 This decorator can be used to perform additional verification of
1738 1739 sub-options and to change the type of sub-options.
1739 1740 """
1740 1741 def register(func):
1741 1742 _pathsuboptions[option] = (attr, func)
1742 1743 return func
1743 1744 return register
1744 1745
1745 1746 @pathsuboption('pushurl', 'pushloc')
1746 1747 def pushurlpathoption(ui, path, value):
1747 1748 u = util.url(value)
1748 1749 # Actually require a URL.
1749 1750 if not u.scheme:
1750 1751 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1751 1752 return None
1752 1753
1753 1754 # Don't support the #foo syntax in the push URL to declare branch to
1754 1755 # push.
1755 1756 if u.fragment:
1756 1757 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1757 1758 'ignoring)\n') % path.name)
1758 1759 u.fragment = None
1759 1760
1760 1761 return bytes(u)
1761 1762
1762 1763 @pathsuboption('pushrev', 'pushrev')
1763 1764 def pushrevpathoption(ui, path, value):
1764 1765 return value
1765 1766
1766 1767 class path(object):
1767 1768 """Represents an individual path and its configuration."""
1768 1769
1769 1770 def __init__(self, ui, name, rawloc=None, suboptions=None):
1770 1771 """Construct a path from its config options.
1771 1772
1772 1773 ``ui`` is the ``ui`` instance the path is coming from.
1773 1774 ``name`` is the symbolic name of the path.
1774 1775 ``rawloc`` is the raw location, as defined in the config.
1775 1776 ``pushloc`` is the raw locations pushes should be made to.
1776 1777
1777 1778 If ``name`` is not defined, we require that the location be a) a local
1778 1779 filesystem path with a .hg directory or b) a URL. If not,
1779 1780 ``ValueError`` is raised.
1780 1781 """
1781 1782 if not rawloc:
1782 1783 raise ValueError('rawloc must be defined')
1783 1784
1784 1785 # Locations may define branches via syntax <base>#<branch>.
1785 1786 u = util.url(rawloc)
1786 1787 branch = None
1787 1788 if u.fragment:
1788 1789 branch = u.fragment
1789 1790 u.fragment = None
1790 1791
1791 1792 self.url = u
1792 1793 self.branch = branch
1793 1794
1794 1795 self.name = name
1795 1796 self.rawloc = rawloc
1796 1797 self.loc = '%s' % u
1797 1798
1798 1799 # When given a raw location but not a symbolic name, validate the
1799 1800 # location is valid.
1800 1801 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1801 1802 raise ValueError('location is not a URL or path to a local '
1802 1803 'repo: %s' % rawloc)
1803 1804
1804 1805 suboptions = suboptions or {}
1805 1806
1806 1807 # Now process the sub-options. If a sub-option is registered, its
1807 1808 # attribute will always be present. The value will be None if there
1808 1809 # was no valid sub-option.
1809 1810 for suboption, (attr, func) in _pathsuboptions.iteritems():
1810 1811 if suboption not in suboptions:
1811 1812 setattr(self, attr, None)
1812 1813 continue
1813 1814
1814 1815 value = func(ui, self, suboptions[suboption])
1815 1816 setattr(self, attr, value)
1816 1817
1817 1818 def _isvalidlocalpath(self, path):
1818 1819 """Returns True if the given path is a potentially valid repository.
1819 1820 This is its own function so that extensions can change the definition of
1820 1821 'valid' in this case (like when pulling from a git repo into a hg
1821 1822 one)."""
1822 1823 return os.path.isdir(os.path.join(path, '.hg'))
1823 1824
1824 1825 @property
1825 1826 def suboptions(self):
1826 1827 """Return sub-options and their values for this path.
1827 1828
1828 1829 This is intended to be used for presentation purposes.
1829 1830 """
1830 1831 d = {}
1831 1832 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1832 1833 value = getattr(self, attr)
1833 1834 if value is not None:
1834 1835 d[subopt] = value
1835 1836 return d
1836 1837
1837 1838 # we instantiate one globally shared progress bar to avoid
1838 1839 # competing progress bars when multiple UI objects get created
1839 1840 _progresssingleton = None
1840 1841
1841 1842 def getprogbar(ui):
1842 1843 global _progresssingleton
1843 1844 if _progresssingleton is None:
1844 1845 # passing 'ui' object to the singleton is fishy,
1845 1846 # this is how the extension used to work but feel free to rework it.
1846 1847 _progresssingleton = progress.progbar(ui)
1847 1848 return _progresssingleton
1848 1849
1849 1850 def haveprogbar():
1850 1851 return _progresssingleton is not None
General Comments 0
You need to be logged in to leave comments. Login now