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