##// END OF EJS Templates
extensions: prevent a crash on py3 with a `minimumhgversion` str value...
Matt Harbison -
r48829:5caec48d default
parent child Browse files
Show More
@@ -1,957 +1,957 b''
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ast
10 import ast
11 import collections
11 import collections
12 import functools
12 import functools
13 import imp
13 import imp
14 import inspect
14 import inspect
15 import os
15 import os
16
16
17 from .i18n import (
17 from .i18n import (
18 _,
18 _,
19 gettext,
19 gettext,
20 )
20 )
21 from .pycompat import (
21 from .pycompat import (
22 getattr,
22 getattr,
23 open,
23 open,
24 setattr,
24 setattr,
25 )
25 )
26
26
27 from . import (
27 from . import (
28 cmdutil,
28 cmdutil,
29 configitems,
29 configitems,
30 error,
30 error,
31 pycompat,
31 pycompat,
32 util,
32 util,
33 )
33 )
34
34
35 from .utils import stringutil
35 from .utils import stringutil
36
36
37 _extensions = {}
37 _extensions = {}
38 _disabledextensions = {}
38 _disabledextensions = {}
39 _aftercallbacks = {}
39 _aftercallbacks = {}
40 _order = []
40 _order = []
41 _builtin = {
41 _builtin = {
42 b'hbisect',
42 b'hbisect',
43 b'bookmarks',
43 b'bookmarks',
44 b'color',
44 b'color',
45 b'parentrevspec',
45 b'parentrevspec',
46 b'progress',
46 b'progress',
47 b'interhg',
47 b'interhg',
48 b'inotify',
48 b'inotify',
49 b'hgcia',
49 b'hgcia',
50 b'shelve',
50 b'shelve',
51 }
51 }
52
52
53
53
54 def extensions(ui=None):
54 def extensions(ui=None):
55 if ui:
55 if ui:
56
56
57 def enabled(name):
57 def enabled(name):
58 for format in [b'%s', b'hgext.%s']:
58 for format in [b'%s', b'hgext.%s']:
59 conf = ui.config(b'extensions', format % name)
59 conf = ui.config(b'extensions', format % name)
60 if conf is not None and not conf.startswith(b'!'):
60 if conf is not None and not conf.startswith(b'!'):
61 return True
61 return True
62
62
63 else:
63 else:
64 enabled = lambda name: True
64 enabled = lambda name: True
65 for name in _order:
65 for name in _order:
66 module = _extensions[name]
66 module = _extensions[name]
67 if module and enabled(name):
67 if module and enabled(name):
68 yield name, module
68 yield name, module
69
69
70
70
71 def find(name):
71 def find(name):
72 '''return module with given extension name'''
72 '''return module with given extension name'''
73 mod = None
73 mod = None
74 try:
74 try:
75 mod = _extensions[name]
75 mod = _extensions[name]
76 except KeyError:
76 except KeyError:
77 for k, v in pycompat.iteritems(_extensions):
77 for k, v in pycompat.iteritems(_extensions):
78 if k.endswith(b'.' + name) or k.endswith(b'/' + name):
78 if k.endswith(b'.' + name) or k.endswith(b'/' + name):
79 mod = v
79 mod = v
80 break
80 break
81 if not mod:
81 if not mod:
82 raise KeyError(name)
82 raise KeyError(name)
83 return mod
83 return mod
84
84
85
85
86 def loadpath(path, module_name):
86 def loadpath(path, module_name):
87 module_name = module_name.replace(b'.', b'_')
87 module_name = module_name.replace(b'.', b'_')
88 path = util.normpath(util.expandpath(path))
88 path = util.normpath(util.expandpath(path))
89 module_name = pycompat.fsdecode(module_name)
89 module_name = pycompat.fsdecode(module_name)
90 path = pycompat.fsdecode(path)
90 path = pycompat.fsdecode(path)
91 if os.path.isdir(path):
91 if os.path.isdir(path):
92 # module/__init__.py style
92 # module/__init__.py style
93 d, f = os.path.split(path)
93 d, f = os.path.split(path)
94 fd, fpath, desc = imp.find_module(f, [d])
94 fd, fpath, desc = imp.find_module(f, [d])
95 # When https://github.com/python/typeshed/issues/3466 is fixed
95 # When https://github.com/python/typeshed/issues/3466 is fixed
96 # and in a pytype release we can drop this disable.
96 # and in a pytype release we can drop this disable.
97 return imp.load_module(
97 return imp.load_module(
98 module_name, fd, fpath, desc # pytype: disable=wrong-arg-types
98 module_name, fd, fpath, desc # pytype: disable=wrong-arg-types
99 )
99 )
100 else:
100 else:
101 try:
101 try:
102 return imp.load_source(module_name, path)
102 return imp.load_source(module_name, path)
103 except IOError as exc:
103 except IOError as exc:
104 if not exc.filename:
104 if not exc.filename:
105 exc.filename = path # python does not fill this
105 exc.filename = path # python does not fill this
106 raise
106 raise
107
107
108
108
109 def _importh(name):
109 def _importh(name):
110 """import and return the <name> module"""
110 """import and return the <name> module"""
111 mod = __import__(pycompat.sysstr(name))
111 mod = __import__(pycompat.sysstr(name))
112 components = name.split(b'.')
112 components = name.split(b'.')
113 for comp in components[1:]:
113 for comp in components[1:]:
114 mod = getattr(mod, comp)
114 mod = getattr(mod, comp)
115 return mod
115 return mod
116
116
117
117
118 def _importext(name, path=None, reportfunc=None):
118 def _importext(name, path=None, reportfunc=None):
119 if path:
119 if path:
120 # the module will be loaded in sys.modules
120 # the module will be loaded in sys.modules
121 # choose an unique name so that it doesn't
121 # choose an unique name so that it doesn't
122 # conflicts with other modules
122 # conflicts with other modules
123 mod = loadpath(path, b'hgext.%s' % name)
123 mod = loadpath(path, b'hgext.%s' % name)
124 else:
124 else:
125 try:
125 try:
126 mod = _importh(b"hgext.%s" % name)
126 mod = _importh(b"hgext.%s" % name)
127 except ImportError as err:
127 except ImportError as err:
128 if reportfunc:
128 if reportfunc:
129 reportfunc(err, b"hgext.%s" % name, b"hgext3rd.%s" % name)
129 reportfunc(err, b"hgext.%s" % name, b"hgext3rd.%s" % name)
130 try:
130 try:
131 mod = _importh(b"hgext3rd.%s" % name)
131 mod = _importh(b"hgext3rd.%s" % name)
132 except ImportError as err:
132 except ImportError as err:
133 if reportfunc:
133 if reportfunc:
134 reportfunc(err, b"hgext3rd.%s" % name, name)
134 reportfunc(err, b"hgext3rd.%s" % name, name)
135 mod = _importh(name)
135 mod = _importh(name)
136 return mod
136 return mod
137
137
138
138
139 def _reportimporterror(ui, err, failed, next):
139 def _reportimporterror(ui, err, failed, next):
140 # note: this ui.log happens before --debug is processed,
140 # note: this ui.log happens before --debug is processed,
141 # Use --config ui.debug=1 to see them.
141 # Use --config ui.debug=1 to see them.
142 ui.log(
142 ui.log(
143 b'extension',
143 b'extension',
144 b' - could not import %s (%s): trying %s\n',
144 b' - could not import %s (%s): trying %s\n',
145 failed,
145 failed,
146 stringutil.forcebytestr(err),
146 stringutil.forcebytestr(err),
147 next,
147 next,
148 )
148 )
149 if ui.debugflag and ui.configbool(b'devel', b'debug.extensions'):
149 if ui.debugflag and ui.configbool(b'devel', b'debug.extensions'):
150 ui.traceback()
150 ui.traceback()
151
151
152
152
153 def _rejectunicode(name, xs):
153 def _rejectunicode(name, xs):
154 if isinstance(xs, (list, set, tuple)):
154 if isinstance(xs, (list, set, tuple)):
155 for x in xs:
155 for x in xs:
156 _rejectunicode(name, x)
156 _rejectunicode(name, x)
157 elif isinstance(xs, dict):
157 elif isinstance(xs, dict):
158 for k, v in xs.items():
158 for k, v in xs.items():
159 _rejectunicode(name, k)
159 _rejectunicode(name, k)
160 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
160 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
161 elif isinstance(xs, type(u'')):
161 elif isinstance(xs, type(u'')):
162 raise error.ProgrammingError(
162 raise error.ProgrammingError(
163 b"unicode %r found in %s" % (xs, name),
163 b"unicode %r found in %s" % (xs, name),
164 hint=b"use b'' to make it byte string",
164 hint=b"use b'' to make it byte string",
165 )
165 )
166
166
167
167
168 # attributes set by registrar.command
168 # attributes set by registrar.command
169 _cmdfuncattrs = (b'norepo', b'optionalrepo', b'inferrepo')
169 _cmdfuncattrs = (b'norepo', b'optionalrepo', b'inferrepo')
170
170
171
171
172 def _validatecmdtable(ui, cmdtable):
172 def _validatecmdtable(ui, cmdtable):
173 """Check if extension commands have required attributes"""
173 """Check if extension commands have required attributes"""
174 for c, e in pycompat.iteritems(cmdtable):
174 for c, e in pycompat.iteritems(cmdtable):
175 f = e[0]
175 f = e[0]
176 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
176 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
177 if not missing:
177 if not missing:
178 continue
178 continue
179 raise error.ProgrammingError(
179 raise error.ProgrammingError(
180 b'missing attributes: %s' % b', '.join(missing),
180 b'missing attributes: %s' % b', '.join(missing),
181 hint=b"use @command decorator to register '%s'" % c,
181 hint=b"use @command decorator to register '%s'" % c,
182 )
182 )
183
183
184
184
185 def _validatetables(ui, mod):
185 def _validatetables(ui, mod):
186 """Sanity check for loadable tables provided by extension module"""
186 """Sanity check for loadable tables provided by extension module"""
187 for t in [b'cmdtable', b'colortable', b'configtable']:
187 for t in [b'cmdtable', b'colortable', b'configtable']:
188 _rejectunicode(t, getattr(mod, t, {}))
188 _rejectunicode(t, getattr(mod, t, {}))
189 for t in [
189 for t in [
190 b'filesetpredicate',
190 b'filesetpredicate',
191 b'internalmerge',
191 b'internalmerge',
192 b'revsetpredicate',
192 b'revsetpredicate',
193 b'templatefilter',
193 b'templatefilter',
194 b'templatefunc',
194 b'templatefunc',
195 b'templatekeyword',
195 b'templatekeyword',
196 ]:
196 ]:
197 o = getattr(mod, t, None)
197 o = getattr(mod, t, None)
198 if o:
198 if o:
199 _rejectunicode(t, o._table)
199 _rejectunicode(t, o._table)
200 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
200 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
201
201
202
202
203 def load(ui, name, path, loadingtime=None):
203 def load(ui, name, path, loadingtime=None):
204 if name.startswith(b'hgext.') or name.startswith(b'hgext/'):
204 if name.startswith(b'hgext.') or name.startswith(b'hgext/'):
205 shortname = name[6:]
205 shortname = name[6:]
206 else:
206 else:
207 shortname = name
207 shortname = name
208 if shortname in _builtin:
208 if shortname in _builtin:
209 return None
209 return None
210 if shortname in _extensions:
210 if shortname in _extensions:
211 return _extensions[shortname]
211 return _extensions[shortname]
212 ui.log(b'extension', b' - loading extension: %s\n', shortname)
212 ui.log(b'extension', b' - loading extension: %s\n', shortname)
213 _extensions[shortname] = None
213 _extensions[shortname] = None
214 with util.timedcm('load extension %s', shortname) as stats:
214 with util.timedcm('load extension %s', shortname) as stats:
215 mod = _importext(name, path, bind(_reportimporterror, ui))
215 mod = _importext(name, path, bind(_reportimporterror, ui))
216 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats)
216 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats)
217 if loadingtime is not None:
217 if loadingtime is not None:
218 loadingtime[shortname] += stats.elapsed
218 loadingtime[shortname] += stats.elapsed
219
219
220 # Before we do anything with the extension, check against minimum stated
220 # Before we do anything with the extension, check against minimum stated
221 # compatibility. This gives extension authors a mechanism to have their
221 # compatibility. This gives extension authors a mechanism to have their
222 # extensions short circuit when loaded with a known incompatible version
222 # extensions short circuit when loaded with a known incompatible version
223 # of Mercurial.
223 # of Mercurial.
224 minver = getattr(mod, 'minimumhgversion', None)
224 minver = getattr(mod, 'minimumhgversion', None)
225 if minver:
225 if minver:
226 curver = util.versiontuple(n=2)
226 curver = util.versiontuple(n=2)
227 extmin = util.versiontuple(minver, 2)
227 extmin = util.versiontuple(stringutil.forcebytestr(minver), 2)
228
228
229 if None in extmin:
229 if None in extmin:
230 extmin = (extmin[0] or 0, extmin[1] or 0)
230 extmin = (extmin[0] or 0, extmin[1] or 0)
231
231
232 if None in curver or extmin > curver:
232 if None in curver or extmin > curver:
233 msg = _(
233 msg = _(
234 b'(third party extension %s requires version %s or newer '
234 b'(third party extension %s requires version %s or newer '
235 b'of Mercurial (current: %s); disabling)\n'
235 b'of Mercurial (current: %s); disabling)\n'
236 )
236 )
237 ui.warn(msg % (shortname, minver, util.version()))
237 ui.warn(msg % (shortname, minver, util.version()))
238 return
238 return
239 ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
239 ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
240 _validatetables(ui, mod)
240 _validatetables(ui, mod)
241
241
242 _extensions[shortname] = mod
242 _extensions[shortname] = mod
243 _order.append(shortname)
243 _order.append(shortname)
244 ui.log(
244 ui.log(
245 b'extension', b' - invoking registered callbacks: %s\n', shortname
245 b'extension', b' - invoking registered callbacks: %s\n', shortname
246 )
246 )
247 with util.timedcm('callbacks extension %s', shortname) as stats:
247 with util.timedcm('callbacks extension %s', shortname) as stats:
248 for fn in _aftercallbacks.get(shortname, []):
248 for fn in _aftercallbacks.get(shortname, []):
249 fn(loaded=True)
249 fn(loaded=True)
250 ui.log(b'extension', b' > callbacks completed in %s\n', stats)
250 ui.log(b'extension', b' > callbacks completed in %s\n', stats)
251 return mod
251 return mod
252
252
253
253
254 def _runuisetup(name, ui):
254 def _runuisetup(name, ui):
255 uisetup = getattr(_extensions[name], 'uisetup', None)
255 uisetup = getattr(_extensions[name], 'uisetup', None)
256 if uisetup:
256 if uisetup:
257 try:
257 try:
258 uisetup(ui)
258 uisetup(ui)
259 except Exception as inst:
259 except Exception as inst:
260 ui.traceback(force=True)
260 ui.traceback(force=True)
261 msg = stringutil.forcebytestr(inst)
261 msg = stringutil.forcebytestr(inst)
262 ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
262 ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
263 return False
263 return False
264 return True
264 return True
265
265
266
266
267 def _runextsetup(name, ui):
267 def _runextsetup(name, ui):
268 extsetup = getattr(_extensions[name], 'extsetup', None)
268 extsetup = getattr(_extensions[name], 'extsetup', None)
269 if extsetup:
269 if extsetup:
270 try:
270 try:
271 extsetup(ui)
271 extsetup(ui)
272 except Exception as inst:
272 except Exception as inst:
273 ui.traceback(force=True)
273 ui.traceback(force=True)
274 msg = stringutil.forcebytestr(inst)
274 msg = stringutil.forcebytestr(inst)
275 ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
275 ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
276 return False
276 return False
277 return True
277 return True
278
278
279
279
280 def loadall(ui, whitelist=None):
280 def loadall(ui, whitelist=None):
281 loadingtime = collections.defaultdict(int)
281 loadingtime = collections.defaultdict(int)
282 result = ui.configitems(b"extensions")
282 result = ui.configitems(b"extensions")
283 if whitelist is not None:
283 if whitelist is not None:
284 result = [(k, v) for (k, v) in result if k in whitelist]
284 result = [(k, v) for (k, v) in result if k in whitelist]
285 newindex = len(_order)
285 newindex = len(_order)
286 ui.log(
286 ui.log(
287 b'extension',
287 b'extension',
288 b'loading %sextensions\n',
288 b'loading %sextensions\n',
289 b'additional ' if newindex else b'',
289 b'additional ' if newindex else b'',
290 )
290 )
291 ui.log(b'extension', b'- processing %d entries\n', len(result))
291 ui.log(b'extension', b'- processing %d entries\n', len(result))
292 with util.timedcm('load all extensions') as stats:
292 with util.timedcm('load all extensions') as stats:
293 for (name, path) in result:
293 for (name, path) in result:
294 if path:
294 if path:
295 if path[0:1] == b'!':
295 if path[0:1] == b'!':
296 if name not in _disabledextensions:
296 if name not in _disabledextensions:
297 ui.log(
297 ui.log(
298 b'extension',
298 b'extension',
299 b' - skipping disabled extension: %s\n',
299 b' - skipping disabled extension: %s\n',
300 name,
300 name,
301 )
301 )
302 _disabledextensions[name] = path[1:]
302 _disabledextensions[name] = path[1:]
303 continue
303 continue
304 try:
304 try:
305 load(ui, name, path, loadingtime)
305 load(ui, name, path, loadingtime)
306 except Exception as inst:
306 except Exception as inst:
307 msg = stringutil.forcebytestr(inst)
307 msg = stringutil.forcebytestr(inst)
308 if path:
308 if path:
309 ui.warn(
309 ui.warn(
310 _(b"*** failed to import extension %s from %s: %s\n")
310 _(b"*** failed to import extension %s from %s: %s\n")
311 % (name, path, msg)
311 % (name, path, msg)
312 )
312 )
313 else:
313 else:
314 ui.warn(
314 ui.warn(
315 _(b"*** failed to import extension %s: %s\n")
315 _(b"*** failed to import extension %s: %s\n")
316 % (name, msg)
316 % (name, msg)
317 )
317 )
318 if isinstance(inst, error.Hint) and inst.hint:
318 if isinstance(inst, error.Hint) and inst.hint:
319 ui.warn(_(b"*** (%s)\n") % inst.hint)
319 ui.warn(_(b"*** (%s)\n") % inst.hint)
320 ui.traceback()
320 ui.traceback()
321
321
322 ui.log(
322 ui.log(
323 b'extension',
323 b'extension',
324 b'> loaded %d extensions, total time %s\n',
324 b'> loaded %d extensions, total time %s\n',
325 len(_order) - newindex,
325 len(_order) - newindex,
326 stats,
326 stats,
327 )
327 )
328 # list of (objname, loadermod, loadername) tuple:
328 # list of (objname, loadermod, loadername) tuple:
329 # - objname is the name of an object in extension module,
329 # - objname is the name of an object in extension module,
330 # from which extra information is loaded
330 # from which extra information is loaded
331 # - loadermod is the module where loader is placed
331 # - loadermod is the module where loader is placed
332 # - loadername is the name of the function,
332 # - loadername is the name of the function,
333 # which takes (ui, extensionname, extraobj) arguments
333 # which takes (ui, extensionname, extraobj) arguments
334 #
334 #
335 # This one is for the list of item that must be run before running any setup
335 # This one is for the list of item that must be run before running any setup
336 earlyextraloaders = [
336 earlyextraloaders = [
337 (b'configtable', configitems, b'loadconfigtable'),
337 (b'configtable', configitems, b'loadconfigtable'),
338 ]
338 ]
339
339
340 ui.log(b'extension', b'- loading configtable attributes\n')
340 ui.log(b'extension', b'- loading configtable attributes\n')
341 _loadextra(ui, newindex, earlyextraloaders)
341 _loadextra(ui, newindex, earlyextraloaders)
342
342
343 broken = set()
343 broken = set()
344 ui.log(b'extension', b'- executing uisetup hooks\n')
344 ui.log(b'extension', b'- executing uisetup hooks\n')
345 with util.timedcm('all uisetup') as alluisetupstats:
345 with util.timedcm('all uisetup') as alluisetupstats:
346 for name in _order[newindex:]:
346 for name in _order[newindex:]:
347 ui.log(b'extension', b' - running uisetup for %s\n', name)
347 ui.log(b'extension', b' - running uisetup for %s\n', name)
348 with util.timedcm('uisetup %s', name) as stats:
348 with util.timedcm('uisetup %s', name) as stats:
349 if not _runuisetup(name, ui):
349 if not _runuisetup(name, ui):
350 ui.log(
350 ui.log(
351 b'extension',
351 b'extension',
352 b' - the %s extension uisetup failed\n',
352 b' - the %s extension uisetup failed\n',
353 name,
353 name,
354 )
354 )
355 broken.add(name)
355 broken.add(name)
356 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
356 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
357 loadingtime[name] += stats.elapsed
357 loadingtime[name] += stats.elapsed
358 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
358 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
359
359
360 ui.log(b'extension', b'- executing extsetup hooks\n')
360 ui.log(b'extension', b'- executing extsetup hooks\n')
361 with util.timedcm('all extsetup') as allextetupstats:
361 with util.timedcm('all extsetup') as allextetupstats:
362 for name in _order[newindex:]:
362 for name in _order[newindex:]:
363 if name in broken:
363 if name in broken:
364 continue
364 continue
365 ui.log(b'extension', b' - running extsetup for %s\n', name)
365 ui.log(b'extension', b' - running extsetup for %s\n', name)
366 with util.timedcm('extsetup %s', name) as stats:
366 with util.timedcm('extsetup %s', name) as stats:
367 if not _runextsetup(name, ui):
367 if not _runextsetup(name, ui):
368 ui.log(
368 ui.log(
369 b'extension',
369 b'extension',
370 b' - the %s extension extsetup failed\n',
370 b' - the %s extension extsetup failed\n',
371 name,
371 name,
372 )
372 )
373 broken.add(name)
373 broken.add(name)
374 ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
374 ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
375 loadingtime[name] += stats.elapsed
375 loadingtime[name] += stats.elapsed
376 ui.log(b'extension', b'> all extsetup took %s\n', allextetupstats)
376 ui.log(b'extension', b'> all extsetup took %s\n', allextetupstats)
377
377
378 for name in broken:
378 for name in broken:
379 ui.log(b'extension', b' - disabling broken %s extension\n', name)
379 ui.log(b'extension', b' - disabling broken %s extension\n', name)
380 _extensions[name] = None
380 _extensions[name] = None
381
381
382 # Call aftercallbacks that were never met.
382 # Call aftercallbacks that were never met.
383 ui.log(b'extension', b'- executing remaining aftercallbacks\n')
383 ui.log(b'extension', b'- executing remaining aftercallbacks\n')
384 with util.timedcm('aftercallbacks') as stats:
384 with util.timedcm('aftercallbacks') as stats:
385 for shortname in _aftercallbacks:
385 for shortname in _aftercallbacks:
386 if shortname in _extensions:
386 if shortname in _extensions:
387 continue
387 continue
388
388
389 for fn in _aftercallbacks[shortname]:
389 for fn in _aftercallbacks[shortname]:
390 ui.log(
390 ui.log(
391 b'extension',
391 b'extension',
392 b' - extension %s not loaded, notify callbacks\n',
392 b' - extension %s not loaded, notify callbacks\n',
393 shortname,
393 shortname,
394 )
394 )
395 fn(loaded=False)
395 fn(loaded=False)
396 ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
396 ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
397
397
398 # loadall() is called multiple times and lingering _aftercallbacks
398 # loadall() is called multiple times and lingering _aftercallbacks
399 # entries could result in double execution. See issue4646.
399 # entries could result in double execution. See issue4646.
400 _aftercallbacks.clear()
400 _aftercallbacks.clear()
401
401
402 # delay importing avoids cyclic dependency (especially commands)
402 # delay importing avoids cyclic dependency (especially commands)
403 from . import (
403 from . import (
404 color,
404 color,
405 commands,
405 commands,
406 filemerge,
406 filemerge,
407 fileset,
407 fileset,
408 revset,
408 revset,
409 templatefilters,
409 templatefilters,
410 templatefuncs,
410 templatefuncs,
411 templatekw,
411 templatekw,
412 )
412 )
413
413
414 # list of (objname, loadermod, loadername) tuple:
414 # list of (objname, loadermod, loadername) tuple:
415 # - objname is the name of an object in extension module,
415 # - objname is the name of an object in extension module,
416 # from which extra information is loaded
416 # from which extra information is loaded
417 # - loadermod is the module where loader is placed
417 # - loadermod is the module where loader is placed
418 # - loadername is the name of the function,
418 # - loadername is the name of the function,
419 # which takes (ui, extensionname, extraobj) arguments
419 # which takes (ui, extensionname, extraobj) arguments
420 ui.log(b'extension', b'- loading extension registration objects\n')
420 ui.log(b'extension', b'- loading extension registration objects\n')
421 extraloaders = [
421 extraloaders = [
422 (b'cmdtable', commands, b'loadcmdtable'),
422 (b'cmdtable', commands, b'loadcmdtable'),
423 (b'colortable', color, b'loadcolortable'),
423 (b'colortable', color, b'loadcolortable'),
424 (b'filesetpredicate', fileset, b'loadpredicate'),
424 (b'filesetpredicate', fileset, b'loadpredicate'),
425 (b'internalmerge', filemerge, b'loadinternalmerge'),
425 (b'internalmerge', filemerge, b'loadinternalmerge'),
426 (b'revsetpredicate', revset, b'loadpredicate'),
426 (b'revsetpredicate', revset, b'loadpredicate'),
427 (b'templatefilter', templatefilters, b'loadfilter'),
427 (b'templatefilter', templatefilters, b'loadfilter'),
428 (b'templatefunc', templatefuncs, b'loadfunction'),
428 (b'templatefunc', templatefuncs, b'loadfunction'),
429 (b'templatekeyword', templatekw, b'loadkeyword'),
429 (b'templatekeyword', templatekw, b'loadkeyword'),
430 ]
430 ]
431 with util.timedcm('load registration objects') as stats:
431 with util.timedcm('load registration objects') as stats:
432 _loadextra(ui, newindex, extraloaders)
432 _loadextra(ui, newindex, extraloaders)
433 ui.log(
433 ui.log(
434 b'extension',
434 b'extension',
435 b'> extension registration object loading took %s\n',
435 b'> extension registration object loading took %s\n',
436 stats,
436 stats,
437 )
437 )
438
438
439 # Report per extension loading time (except reposetup)
439 # Report per extension loading time (except reposetup)
440 for name in sorted(loadingtime):
440 for name in sorted(loadingtime):
441 ui.log(
441 ui.log(
442 b'extension',
442 b'extension',
443 b'> extension %s take a total of %s to load\n',
443 b'> extension %s take a total of %s to load\n',
444 name,
444 name,
445 util.timecount(loadingtime[name]),
445 util.timecount(loadingtime[name]),
446 )
446 )
447
447
448 ui.log(b'extension', b'extension loading complete\n')
448 ui.log(b'extension', b'extension loading complete\n')
449
449
450
450
451 def _loadextra(ui, newindex, extraloaders):
451 def _loadextra(ui, newindex, extraloaders):
452 for name in _order[newindex:]:
452 for name in _order[newindex:]:
453 module = _extensions[name]
453 module = _extensions[name]
454 if not module:
454 if not module:
455 continue # loading this module failed
455 continue # loading this module failed
456
456
457 for objname, loadermod, loadername in extraloaders:
457 for objname, loadermod, loadername in extraloaders:
458 extraobj = getattr(module, objname, None)
458 extraobj = getattr(module, objname, None)
459 if extraobj is not None:
459 if extraobj is not None:
460 getattr(loadermod, loadername)(ui, name, extraobj)
460 getattr(loadermod, loadername)(ui, name, extraobj)
461
461
462
462
463 def afterloaded(extension, callback):
463 def afterloaded(extension, callback):
464 """Run the specified function after a named extension is loaded.
464 """Run the specified function after a named extension is loaded.
465
465
466 If the named extension is already loaded, the callback will be called
466 If the named extension is already loaded, the callback will be called
467 immediately.
467 immediately.
468
468
469 If the named extension never loads, the callback will be called after
469 If the named extension never loads, the callback will be called after
470 all extensions have been loaded.
470 all extensions have been loaded.
471
471
472 The callback receives the named argument ``loaded``, which is a boolean
472 The callback receives the named argument ``loaded``, which is a boolean
473 indicating whether the dependent extension actually loaded.
473 indicating whether the dependent extension actually loaded.
474 """
474 """
475
475
476 if extension in _extensions:
476 if extension in _extensions:
477 # Report loaded as False if the extension is disabled
477 # Report loaded as False if the extension is disabled
478 loaded = _extensions[extension] is not None
478 loaded = _extensions[extension] is not None
479 callback(loaded=loaded)
479 callback(loaded=loaded)
480 else:
480 else:
481 _aftercallbacks.setdefault(extension, []).append(callback)
481 _aftercallbacks.setdefault(extension, []).append(callback)
482
482
483
483
484 def populateui(ui):
484 def populateui(ui):
485 """Run extension hooks on the given ui to populate additional members,
485 """Run extension hooks on the given ui to populate additional members,
486 extend the class dynamically, etc.
486 extend the class dynamically, etc.
487
487
488 This will be called after the configuration is loaded, and/or extensions
488 This will be called after the configuration is loaded, and/or extensions
489 are loaded. In general, it's once per ui instance, but in command-server
489 are loaded. In general, it's once per ui instance, but in command-server
490 and hgweb, this may be called more than once with the same ui.
490 and hgweb, this may be called more than once with the same ui.
491 """
491 """
492 for name, mod in extensions(ui):
492 for name, mod in extensions(ui):
493 hook = getattr(mod, 'uipopulate', None)
493 hook = getattr(mod, 'uipopulate', None)
494 if not hook:
494 if not hook:
495 continue
495 continue
496 try:
496 try:
497 hook(ui)
497 hook(ui)
498 except Exception as inst:
498 except Exception as inst:
499 ui.traceback(force=True)
499 ui.traceback(force=True)
500 ui.warn(
500 ui.warn(
501 _(b'*** failed to populate ui by extension %s: %s\n')
501 _(b'*** failed to populate ui by extension %s: %s\n')
502 % (name, stringutil.forcebytestr(inst))
502 % (name, stringutil.forcebytestr(inst))
503 )
503 )
504
504
505
505
506 def bind(func, *args):
506 def bind(func, *args):
507 """Partial function application
507 """Partial function application
508
508
509 Returns a new function that is the partial application of args and kwargs
509 Returns a new function that is the partial application of args and kwargs
510 to func. For example,
510 to func. For example,
511
511
512 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)"""
512 f(1, 2, bar=3) === bind(f, 1)(2, bar=3)"""
513 assert callable(func)
513 assert callable(func)
514
514
515 def closure(*a, **kw):
515 def closure(*a, **kw):
516 return func(*(args + a), **kw)
516 return func(*(args + a), **kw)
517
517
518 return closure
518 return closure
519
519
520
520
521 def _updatewrapper(wrap, origfn, unboundwrapper):
521 def _updatewrapper(wrap, origfn, unboundwrapper):
522 '''Copy and add some useful attributes to wrapper'''
522 '''Copy and add some useful attributes to wrapper'''
523 try:
523 try:
524 wrap.__name__ = origfn.__name__
524 wrap.__name__ = origfn.__name__
525 except AttributeError:
525 except AttributeError:
526 pass
526 pass
527 wrap.__module__ = getattr(origfn, '__module__')
527 wrap.__module__ = getattr(origfn, '__module__')
528 wrap.__doc__ = getattr(origfn, '__doc__')
528 wrap.__doc__ = getattr(origfn, '__doc__')
529 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
529 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
530 wrap._origfunc = origfn
530 wrap._origfunc = origfn
531 wrap._unboundwrapper = unboundwrapper
531 wrap._unboundwrapper = unboundwrapper
532
532
533
533
534 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
534 def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
535 '''Wrap the command named `command' in table
535 '''Wrap the command named `command' in table
536
536
537 Replace command in the command table with wrapper. The wrapped command will
537 Replace command in the command table with wrapper. The wrapped command will
538 be inserted into the command table specified by the table argument.
538 be inserted into the command table specified by the table argument.
539
539
540 The wrapper will be called like
540 The wrapper will be called like
541
541
542 wrapper(orig, *args, **kwargs)
542 wrapper(orig, *args, **kwargs)
543
543
544 where orig is the original (wrapped) function, and *args, **kwargs
544 where orig is the original (wrapped) function, and *args, **kwargs
545 are the arguments passed to it.
545 are the arguments passed to it.
546
546
547 Optionally append to the command synopsis and docstring, used for help.
547 Optionally append to the command synopsis and docstring, used for help.
548 For example, if your extension wraps the ``bookmarks`` command to add the
548 For example, if your extension wraps the ``bookmarks`` command to add the
549 flags ``--remote`` and ``--all`` you might call this function like so:
549 flags ``--remote`` and ``--all`` you might call this function like so:
550
550
551 synopsis = ' [-a] [--remote]'
551 synopsis = ' [-a] [--remote]'
552 docstring = """
552 docstring = """
553
553
554 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
554 The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
555 flags to the bookmarks command. Either flag will show the remote bookmarks
555 flags to the bookmarks command. Either flag will show the remote bookmarks
556 known to the repository; ``--remote`` will also suppress the output of the
556 known to the repository; ``--remote`` will also suppress the output of the
557 local bookmarks.
557 local bookmarks.
558 """
558 """
559
559
560 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
560 extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
561 synopsis, docstring)
561 synopsis, docstring)
562 '''
562 '''
563 assert callable(wrapper)
563 assert callable(wrapper)
564 aliases, entry = cmdutil.findcmd(command, table)
564 aliases, entry = cmdutil.findcmd(command, table)
565 for alias, e in pycompat.iteritems(table):
565 for alias, e in pycompat.iteritems(table):
566 if e is entry:
566 if e is entry:
567 key = alias
567 key = alias
568 break
568 break
569
569
570 origfn = entry[0]
570 origfn = entry[0]
571 wrap = functools.partial(
571 wrap = functools.partial(
572 util.checksignature(wrapper), util.checksignature(origfn)
572 util.checksignature(wrapper), util.checksignature(origfn)
573 )
573 )
574 _updatewrapper(wrap, origfn, wrapper)
574 _updatewrapper(wrap, origfn, wrapper)
575 if docstring is not None:
575 if docstring is not None:
576 wrap.__doc__ += docstring
576 wrap.__doc__ += docstring
577
577
578 newentry = list(entry)
578 newentry = list(entry)
579 newentry[0] = wrap
579 newentry[0] = wrap
580 if synopsis is not None:
580 if synopsis is not None:
581 newentry[2] += synopsis
581 newentry[2] += synopsis
582 table[key] = tuple(newentry)
582 table[key] = tuple(newentry)
583 return entry
583 return entry
584
584
585
585
586 def wrapfilecache(cls, propname, wrapper):
586 def wrapfilecache(cls, propname, wrapper):
587 """Wraps a filecache property.
587 """Wraps a filecache property.
588
588
589 These can't be wrapped using the normal wrapfunction.
589 These can't be wrapped using the normal wrapfunction.
590 """
590 """
591 propname = pycompat.sysstr(propname)
591 propname = pycompat.sysstr(propname)
592 assert callable(wrapper)
592 assert callable(wrapper)
593 for currcls in cls.__mro__:
593 for currcls in cls.__mro__:
594 if propname in currcls.__dict__:
594 if propname in currcls.__dict__:
595 origfn = currcls.__dict__[propname].func
595 origfn = currcls.__dict__[propname].func
596 assert callable(origfn)
596 assert callable(origfn)
597
597
598 def wrap(*args, **kwargs):
598 def wrap(*args, **kwargs):
599 return wrapper(origfn, *args, **kwargs)
599 return wrapper(origfn, *args, **kwargs)
600
600
601 currcls.__dict__[propname].func = wrap
601 currcls.__dict__[propname].func = wrap
602 break
602 break
603
603
604 if currcls is object:
604 if currcls is object:
605 raise AttributeError("type '%s' has no property '%s'" % (cls, propname))
605 raise AttributeError("type '%s' has no property '%s'" % (cls, propname))
606
606
607
607
608 class wrappedfunction(object):
608 class wrappedfunction(object):
609 '''context manager for temporarily wrapping a function'''
609 '''context manager for temporarily wrapping a function'''
610
610
611 def __init__(self, container, funcname, wrapper):
611 def __init__(self, container, funcname, wrapper):
612 assert callable(wrapper)
612 assert callable(wrapper)
613 self._container = container
613 self._container = container
614 self._funcname = funcname
614 self._funcname = funcname
615 self._wrapper = wrapper
615 self._wrapper = wrapper
616
616
617 def __enter__(self):
617 def __enter__(self):
618 wrapfunction(self._container, self._funcname, self._wrapper)
618 wrapfunction(self._container, self._funcname, self._wrapper)
619
619
620 def __exit__(self, exctype, excvalue, traceback):
620 def __exit__(self, exctype, excvalue, traceback):
621 unwrapfunction(self._container, self._funcname, self._wrapper)
621 unwrapfunction(self._container, self._funcname, self._wrapper)
622
622
623
623
624 def wrapfunction(container, funcname, wrapper):
624 def wrapfunction(container, funcname, wrapper):
625 """Wrap the function named funcname in container
625 """Wrap the function named funcname in container
626
626
627 Replace the funcname member in the given container with the specified
627 Replace the funcname member in the given container with the specified
628 wrapper. The container is typically a module, class, or instance.
628 wrapper. The container is typically a module, class, or instance.
629
629
630 The wrapper will be called like
630 The wrapper will be called like
631
631
632 wrapper(orig, *args, **kwargs)
632 wrapper(orig, *args, **kwargs)
633
633
634 where orig is the original (wrapped) function, and *args, **kwargs
634 where orig is the original (wrapped) function, and *args, **kwargs
635 are the arguments passed to it.
635 are the arguments passed to it.
636
636
637 Wrapping methods of the repository object is not recommended since
637 Wrapping methods of the repository object is not recommended since
638 it conflicts with extensions that extend the repository by
638 it conflicts with extensions that extend the repository by
639 subclassing. All extensions that need to extend methods of
639 subclassing. All extensions that need to extend methods of
640 localrepository should use this subclassing trick: namely,
640 localrepository should use this subclassing trick: namely,
641 reposetup() should look like
641 reposetup() should look like
642
642
643 def reposetup(ui, repo):
643 def reposetup(ui, repo):
644 class myrepo(repo.__class__):
644 class myrepo(repo.__class__):
645 def whatever(self, *args, **kwargs):
645 def whatever(self, *args, **kwargs):
646 [...extension stuff...]
646 [...extension stuff...]
647 super(myrepo, self).whatever(*args, **kwargs)
647 super(myrepo, self).whatever(*args, **kwargs)
648 [...extension stuff...]
648 [...extension stuff...]
649
649
650 repo.__class__ = myrepo
650 repo.__class__ = myrepo
651
651
652 In general, combining wrapfunction() with subclassing does not
652 In general, combining wrapfunction() with subclassing does not
653 work. Since you cannot control what other extensions are loaded by
653 work. Since you cannot control what other extensions are loaded by
654 your end users, you should play nicely with others by using the
654 your end users, you should play nicely with others by using the
655 subclass trick.
655 subclass trick.
656 """
656 """
657 assert callable(wrapper)
657 assert callable(wrapper)
658
658
659 origfn = getattr(container, funcname)
659 origfn = getattr(container, funcname)
660 assert callable(origfn)
660 assert callable(origfn)
661 if inspect.ismodule(container):
661 if inspect.ismodule(container):
662 # origfn is not an instance or class method. "partial" can be used.
662 # origfn is not an instance or class method. "partial" can be used.
663 # "partial" won't insert a frame in traceback.
663 # "partial" won't insert a frame in traceback.
664 wrap = functools.partial(wrapper, origfn)
664 wrap = functools.partial(wrapper, origfn)
665 else:
665 else:
666 # "partial" cannot be safely used. Emulate its effect by using "bind".
666 # "partial" cannot be safely used. Emulate its effect by using "bind".
667 # The downside is one more frame in traceback.
667 # The downside is one more frame in traceback.
668 wrap = bind(wrapper, origfn)
668 wrap = bind(wrapper, origfn)
669 _updatewrapper(wrap, origfn, wrapper)
669 _updatewrapper(wrap, origfn, wrapper)
670 setattr(container, funcname, wrap)
670 setattr(container, funcname, wrap)
671 return origfn
671 return origfn
672
672
673
673
674 def unwrapfunction(container, funcname, wrapper=None):
674 def unwrapfunction(container, funcname, wrapper=None):
675 """undo wrapfunction
675 """undo wrapfunction
676
676
677 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
677 If wrappers is None, undo the last wrap. Otherwise removes the wrapper
678 from the chain of wrappers.
678 from the chain of wrappers.
679
679
680 Return the removed wrapper.
680 Return the removed wrapper.
681 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
681 Raise IndexError if wrapper is None and nothing to unwrap; ValueError if
682 wrapper is not None but is not found in the wrapper chain.
682 wrapper is not None but is not found in the wrapper chain.
683 """
683 """
684 chain = getwrapperchain(container, funcname)
684 chain = getwrapperchain(container, funcname)
685 origfn = chain.pop()
685 origfn = chain.pop()
686 if wrapper is None:
686 if wrapper is None:
687 wrapper = chain[0]
687 wrapper = chain[0]
688 chain.remove(wrapper)
688 chain.remove(wrapper)
689 setattr(container, funcname, origfn)
689 setattr(container, funcname, origfn)
690 for w in reversed(chain):
690 for w in reversed(chain):
691 wrapfunction(container, funcname, w)
691 wrapfunction(container, funcname, w)
692 return wrapper
692 return wrapper
693
693
694
694
695 def getwrapperchain(container, funcname):
695 def getwrapperchain(container, funcname):
696 """get a chain of wrappers of a function
696 """get a chain of wrappers of a function
697
697
698 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
698 Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc]
699
699
700 The wrapper functions are the ones passed to wrapfunction, whose first
700 The wrapper functions are the ones passed to wrapfunction, whose first
701 argument is origfunc.
701 argument is origfunc.
702 """
702 """
703 result = []
703 result = []
704 fn = getattr(container, funcname)
704 fn = getattr(container, funcname)
705 while fn:
705 while fn:
706 assert callable(fn)
706 assert callable(fn)
707 result.append(getattr(fn, '_unboundwrapper', fn))
707 result.append(getattr(fn, '_unboundwrapper', fn))
708 fn = getattr(fn, '_origfunc', None)
708 fn = getattr(fn, '_origfunc', None)
709 return result
709 return result
710
710
711
711
712 def _disabledpaths():
712 def _disabledpaths():
713 '''find paths of disabled extensions. returns a dict of {name: path}'''
713 '''find paths of disabled extensions. returns a dict of {name: path}'''
714 import hgext
714 import hgext
715
715
716 # The hgext might not have a __file__ attribute (e.g. in PyOxidizer) and
716 # The hgext might not have a __file__ attribute (e.g. in PyOxidizer) and
717 # it might not be on a filesystem even if it does.
717 # it might not be on a filesystem even if it does.
718 if util.safehasattr(hgext, '__file__'):
718 if util.safehasattr(hgext, '__file__'):
719 extpath = os.path.dirname(
719 extpath = os.path.dirname(
720 util.abspath(pycompat.fsencode(hgext.__file__))
720 util.abspath(pycompat.fsencode(hgext.__file__))
721 )
721 )
722 try:
722 try:
723 files = os.listdir(extpath)
723 files = os.listdir(extpath)
724 except OSError:
724 except OSError:
725 return {}
725 return {}
726 else:
726 else:
727 return {}
727 return {}
728
728
729 exts = {}
729 exts = {}
730 for e in files:
730 for e in files:
731 if e.endswith(b'.py'):
731 if e.endswith(b'.py'):
732 name = e.rsplit(b'.', 1)[0]
732 name = e.rsplit(b'.', 1)[0]
733 path = os.path.join(extpath, e)
733 path = os.path.join(extpath, e)
734 else:
734 else:
735 name = e
735 name = e
736 path = os.path.join(extpath, e, b'__init__.py')
736 path = os.path.join(extpath, e, b'__init__.py')
737 if not os.path.exists(path):
737 if not os.path.exists(path):
738 continue
738 continue
739 if name in exts or name in _order or name == b'__init__':
739 if name in exts or name in _order or name == b'__init__':
740 continue
740 continue
741 exts[name] = path
741 exts[name] = path
742 for name, path in pycompat.iteritems(_disabledextensions):
742 for name, path in pycompat.iteritems(_disabledextensions):
743 # If no path was provided for a disabled extension (e.g. "color=!"),
743 # If no path was provided for a disabled extension (e.g. "color=!"),
744 # don't replace the path we already found by the scan above.
744 # don't replace the path we already found by the scan above.
745 if path:
745 if path:
746 exts[name] = path
746 exts[name] = path
747 return exts
747 return exts
748
748
749
749
750 def _moduledoc(file):
750 def _moduledoc(file):
751 """return the top-level python documentation for the given file
751 """return the top-level python documentation for the given file
752
752
753 Loosely inspired by pydoc.source_synopsis(), but rewritten to
753 Loosely inspired by pydoc.source_synopsis(), but rewritten to
754 handle triple quotes and to return the whole text instead of just
754 handle triple quotes and to return the whole text instead of just
755 the synopsis"""
755 the synopsis"""
756 result = []
756 result = []
757
757
758 line = file.readline()
758 line = file.readline()
759 while line[:1] == b'#' or not line.strip():
759 while line[:1] == b'#' or not line.strip():
760 line = file.readline()
760 line = file.readline()
761 if not line:
761 if not line:
762 break
762 break
763
763
764 start = line[:3]
764 start = line[:3]
765 if start == b'"""' or start == b"'''":
765 if start == b'"""' or start == b"'''":
766 line = line[3:]
766 line = line[3:]
767 while line:
767 while line:
768 if line.rstrip().endswith(start):
768 if line.rstrip().endswith(start):
769 line = line.split(start)[0]
769 line = line.split(start)[0]
770 if line:
770 if line:
771 result.append(line)
771 result.append(line)
772 break
772 break
773 elif not line:
773 elif not line:
774 return None # unmatched delimiter
774 return None # unmatched delimiter
775 result.append(line)
775 result.append(line)
776 line = file.readline()
776 line = file.readline()
777 else:
777 else:
778 return None
778 return None
779
779
780 return b''.join(result)
780 return b''.join(result)
781
781
782
782
783 def _disabledhelp(path):
783 def _disabledhelp(path):
784 '''retrieve help synopsis of a disabled extension (without importing)'''
784 '''retrieve help synopsis of a disabled extension (without importing)'''
785 try:
785 try:
786 with open(path, b'rb') as src:
786 with open(path, b'rb') as src:
787 doc = _moduledoc(src)
787 doc = _moduledoc(src)
788 except IOError:
788 except IOError:
789 return
789 return
790
790
791 if doc: # extracting localized synopsis
791 if doc: # extracting localized synopsis
792 return gettext(doc)
792 return gettext(doc)
793 else:
793 else:
794 return _(b'(no help text available)')
794 return _(b'(no help text available)')
795
795
796
796
797 def disabled():
797 def disabled():
798 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
798 '''find disabled extensions from hgext. returns a dict of {name: desc}'''
799 try:
799 try:
800 from hgext import __index__ # pytype: disable=import-error
800 from hgext import __index__ # pytype: disable=import-error
801
801
802 return {
802 return {
803 name: gettext(desc)
803 name: gettext(desc)
804 for name, desc in pycompat.iteritems(__index__.docs)
804 for name, desc in pycompat.iteritems(__index__.docs)
805 if name not in _order
805 if name not in _order
806 }
806 }
807 except (ImportError, AttributeError):
807 except (ImportError, AttributeError):
808 pass
808 pass
809
809
810 paths = _disabledpaths()
810 paths = _disabledpaths()
811 if not paths:
811 if not paths:
812 return {}
812 return {}
813
813
814 exts = {}
814 exts = {}
815 for name, path in pycompat.iteritems(paths):
815 for name, path in pycompat.iteritems(paths):
816 doc = _disabledhelp(path)
816 doc = _disabledhelp(path)
817 if doc and name != b'__index__':
817 if doc and name != b'__index__':
818 exts[name] = doc.splitlines()[0]
818 exts[name] = doc.splitlines()[0]
819
819
820 return exts
820 return exts
821
821
822
822
823 def disabled_help(name):
823 def disabled_help(name):
824 """Obtain the full help text for a disabled extension, or None."""
824 """Obtain the full help text for a disabled extension, or None."""
825 paths = _disabledpaths()
825 paths = _disabledpaths()
826 if name in paths:
826 if name in paths:
827 return _disabledhelp(paths[name])
827 return _disabledhelp(paths[name])
828
828
829
829
830 def _walkcommand(node):
830 def _walkcommand(node):
831 """Scan @command() decorators in the tree starting at node"""
831 """Scan @command() decorators in the tree starting at node"""
832 todo = collections.deque([node])
832 todo = collections.deque([node])
833 while todo:
833 while todo:
834 node = todo.popleft()
834 node = todo.popleft()
835 if not isinstance(node, ast.FunctionDef):
835 if not isinstance(node, ast.FunctionDef):
836 todo.extend(ast.iter_child_nodes(node))
836 todo.extend(ast.iter_child_nodes(node))
837 continue
837 continue
838 for d in node.decorator_list:
838 for d in node.decorator_list:
839 if not isinstance(d, ast.Call):
839 if not isinstance(d, ast.Call):
840 continue
840 continue
841 if not isinstance(d.func, ast.Name):
841 if not isinstance(d.func, ast.Name):
842 continue
842 continue
843 if d.func.id != 'command':
843 if d.func.id != 'command':
844 continue
844 continue
845 yield d
845 yield d
846
846
847
847
848 def _disabledcmdtable(path):
848 def _disabledcmdtable(path):
849 """Construct a dummy command table without loading the extension module
849 """Construct a dummy command table without loading the extension module
850
850
851 This may raise IOError or SyntaxError.
851 This may raise IOError or SyntaxError.
852 """
852 """
853 with open(path, b'rb') as src:
853 with open(path, b'rb') as src:
854 root = ast.parse(src.read(), path)
854 root = ast.parse(src.read(), path)
855 cmdtable = {}
855 cmdtable = {}
856 for node in _walkcommand(root):
856 for node in _walkcommand(root):
857 if not node.args:
857 if not node.args:
858 continue
858 continue
859 a = node.args[0]
859 a = node.args[0]
860 if isinstance(a, ast.Str):
860 if isinstance(a, ast.Str):
861 name = pycompat.sysbytes(a.s)
861 name = pycompat.sysbytes(a.s)
862 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
862 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
863 name = a.s
863 name = a.s
864 else:
864 else:
865 continue
865 continue
866 cmdtable[name] = (None, [], b'')
866 cmdtable[name] = (None, [], b'')
867 return cmdtable
867 return cmdtable
868
868
869
869
870 def _finddisabledcmd(ui, cmd, name, path, strict):
870 def _finddisabledcmd(ui, cmd, name, path, strict):
871 try:
871 try:
872 cmdtable = _disabledcmdtable(path)
872 cmdtable = _disabledcmdtable(path)
873 except (IOError, SyntaxError):
873 except (IOError, SyntaxError):
874 return
874 return
875 try:
875 try:
876 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
876 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
877 except (error.AmbiguousCommand, error.UnknownCommand):
877 except (error.AmbiguousCommand, error.UnknownCommand):
878 return
878 return
879 for c in aliases:
879 for c in aliases:
880 if c.startswith(cmd):
880 if c.startswith(cmd):
881 cmd = c
881 cmd = c
882 break
882 break
883 else:
883 else:
884 cmd = aliases[0]
884 cmd = aliases[0]
885 doc = _disabledhelp(path)
885 doc = _disabledhelp(path)
886 return (cmd, name, doc)
886 return (cmd, name, doc)
887
887
888
888
889 def disabledcmd(ui, cmd, strict=False):
889 def disabledcmd(ui, cmd, strict=False):
890 """find cmd from disabled extensions without importing.
890 """find cmd from disabled extensions without importing.
891 returns (cmdname, extname, doc)"""
891 returns (cmdname, extname, doc)"""
892
892
893 paths = _disabledpaths()
893 paths = _disabledpaths()
894 if not paths:
894 if not paths:
895 raise error.UnknownCommand(cmd)
895 raise error.UnknownCommand(cmd)
896
896
897 ext = None
897 ext = None
898 # first, search for an extension with the same name as the command
898 # first, search for an extension with the same name as the command
899 path = paths.pop(cmd, None)
899 path = paths.pop(cmd, None)
900 if path:
900 if path:
901 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
901 ext = _finddisabledcmd(ui, cmd, cmd, path, strict=strict)
902 if not ext:
902 if not ext:
903 # otherwise, interrogate each extension until there's a match
903 # otherwise, interrogate each extension until there's a match
904 for name, path in pycompat.iteritems(paths):
904 for name, path in pycompat.iteritems(paths):
905 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
905 ext = _finddisabledcmd(ui, cmd, name, path, strict=strict)
906 if ext:
906 if ext:
907 break
907 break
908 if ext:
908 if ext:
909 return ext
909 return ext
910
910
911 raise error.UnknownCommand(cmd)
911 raise error.UnknownCommand(cmd)
912
912
913
913
914 def enabled(shortname=True):
914 def enabled(shortname=True):
915 '''return a dict of {name: desc} of extensions'''
915 '''return a dict of {name: desc} of extensions'''
916 exts = {}
916 exts = {}
917 for ename, ext in extensions():
917 for ename, ext in extensions():
918 doc = gettext(ext.__doc__) or _(b'(no help text available)')
918 doc = gettext(ext.__doc__) or _(b'(no help text available)')
919 assert doc is not None # help pytype
919 assert doc is not None # help pytype
920 if shortname:
920 if shortname:
921 ename = ename.split(b'.')[-1]
921 ename = ename.split(b'.')[-1]
922 exts[ename] = doc.splitlines()[0].strip()
922 exts[ename] = doc.splitlines()[0].strip()
923
923
924 return exts
924 return exts
925
925
926
926
927 def notloaded():
927 def notloaded():
928 '''return short names of extensions that failed to load'''
928 '''return short names of extensions that failed to load'''
929 return [
929 return [
930 name for name, mod in pycompat.iteritems(_extensions) if mod is None
930 name for name, mod in pycompat.iteritems(_extensions) if mod is None
931 ]
931 ]
932
932
933
933
934 def moduleversion(module):
934 def moduleversion(module):
935 '''return version information from given module as a string'''
935 '''return version information from given module as a string'''
936 if util.safehasattr(module, b'getversion') and callable(module.getversion):
936 if util.safehasattr(module, b'getversion') and callable(module.getversion):
937 try:
937 try:
938 version = module.getversion()
938 version = module.getversion()
939 except Exception:
939 except Exception:
940 version = b'unknown'
940 version = b'unknown'
941
941
942 elif util.safehasattr(module, b'__version__'):
942 elif util.safehasattr(module, b'__version__'):
943 version = module.__version__
943 version = module.__version__
944 else:
944 else:
945 version = b''
945 version = b''
946 if isinstance(version, (list, tuple)):
946 if isinstance(version, (list, tuple)):
947 version = b'.'.join(pycompat.bytestr(o) for o in version)
947 version = b'.'.join(pycompat.bytestr(o) for o in version)
948 else:
948 else:
949 # version data should be bytes, but not all extensions are ported
949 # version data should be bytes, but not all extensions are ported
950 # to py3.
950 # to py3.
951 version = stringutil.forcebytestr(version)
951 version = stringutil.forcebytestr(version)
952 return version
952 return version
953
953
954
954
955 def ismoduleinternal(module):
955 def ismoduleinternal(module):
956 exttestedwith = getattr(module, 'testedwith', None)
956 exttestedwith = getattr(module, 'testedwith', None)
957 return exttestedwith == b"ships-with-hg-core"
957 return exttestedwith == b"ships-with-hg-core"
@@ -1,1945 +1,1946 b''
1 Test basic extension support
1 Test basic extension support
2 $ cat > unflush.py <<EOF
2 $ cat > unflush.py <<EOF
3 > import sys
3 > import sys
4 > from mercurial import pycompat
4 > from mercurial import pycompat
5 > if pycompat.ispy3:
5 > if pycompat.ispy3:
6 > # no changes required
6 > # no changes required
7 > sys.exit(0)
7 > sys.exit(0)
8 > with open(sys.argv[1], 'rb') as f:
8 > with open(sys.argv[1], 'rb') as f:
9 > data = f.read()
9 > data = f.read()
10 > with open(sys.argv[1], 'wb') as f:
10 > with open(sys.argv[1], 'wb') as f:
11 > f.write(data.replace(b', flush=True', b''))
11 > f.write(data.replace(b', flush=True', b''))
12 > EOF
12 > EOF
13
13
14 $ cat > foobar.py <<EOF
14 $ cat > foobar.py <<EOF
15 > import os
15 > import os
16 > from mercurial import commands, exthelper, registrar
16 > from mercurial import commands, exthelper, registrar
17 >
17 >
18 > eh = exthelper.exthelper()
18 > eh = exthelper.exthelper()
19 > eh.configitem(b'tests', b'foo', default=b"Foo")
19 > eh.configitem(b'tests', b'foo', default=b"Foo")
20 >
20 >
21 > uisetup = eh.finaluisetup
21 > uisetup = eh.finaluisetup
22 > uipopulate = eh.finaluipopulate
22 > uipopulate = eh.finaluipopulate
23 > reposetup = eh.finalreposetup
23 > reposetup = eh.finalreposetup
24 > cmdtable = eh.cmdtable
24 > cmdtable = eh.cmdtable
25 > configtable = eh.configtable
25 > configtable = eh.configtable
26 >
26 >
27 > @eh.uisetup
27 > @eh.uisetup
28 > def _uisetup(ui):
28 > def _uisetup(ui):
29 > ui.debug(b"uisetup called [debug]\\n")
29 > ui.debug(b"uisetup called [debug]\\n")
30 > ui.write(b"uisetup called\\n")
30 > ui.write(b"uisetup called\\n")
31 > ui.status(b"uisetup called [status]\\n")
31 > ui.status(b"uisetup called [status]\\n")
32 > ui.flush()
32 > ui.flush()
33 > @eh.uipopulate
33 > @eh.uipopulate
34 > def _uipopulate(ui):
34 > def _uipopulate(ui):
35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
35 > ui._populatecnt = getattr(ui, "_populatecnt", 0) + 1
36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
36 > ui.write(b"uipopulate called (%d times)\n" % ui._populatecnt)
37 > @eh.reposetup
37 > @eh.reposetup
38 > def _reposetup(ui, repo):
38 > def _reposetup(ui, repo):
39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
39 > ui.write(b"reposetup called for %s\\n" % os.path.basename(repo.root))
40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
40 > ui.write(b"ui %s= repo.ui\\n" % (ui == repo.ui and b"=" or b"!"))
41 > ui.flush()
41 > ui.flush()
42 > @eh.command(b'foo', [], b'hg foo')
42 > @eh.command(b'foo', [], b'hg foo')
43 > def foo(ui, *args, **kwargs):
43 > def foo(ui, *args, **kwargs):
44 > foo = ui.config(b'tests', b'foo')
44 > foo = ui.config(b'tests', b'foo')
45 > ui.write(foo)
45 > ui.write(foo)
46 > ui.write(b"\\n")
46 > ui.write(b"\\n")
47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
47 > @eh.command(b'bar', [], b'hg bar', norepo=True)
48 > def bar(ui, *args, **kwargs):
48 > def bar(ui, *args, **kwargs):
49 > ui.write(b"Bar\\n")
49 > ui.write(b"Bar\\n")
50 > EOF
50 > EOF
51 $ abspath=`pwd`/foobar.py
51 $ abspath=`pwd`/foobar.py
52
52
53 $ mkdir barfoo
53 $ mkdir barfoo
54 $ cp foobar.py barfoo/__init__.py
54 $ cp foobar.py barfoo/__init__.py
55 $ barfoopath=`pwd`/barfoo
55 $ barfoopath=`pwd`/barfoo
56
56
57 $ hg init a
57 $ hg init a
58 $ cd a
58 $ cd a
59 $ echo foo > file
59 $ echo foo > file
60 $ hg add file
60 $ hg add file
61 $ hg commit -m 'add file'
61 $ hg commit -m 'add file'
62
62
63 $ echo '[extensions]' >> $HGRCPATH
63 $ echo '[extensions]' >> $HGRCPATH
64 $ echo "foobar = $abspath" >> $HGRCPATH
64 $ echo "foobar = $abspath" >> $HGRCPATH
65 $ hg foo
65 $ hg foo
66 uisetup called
66 uisetup called
67 uisetup called [status]
67 uisetup called [status]
68 uipopulate called (1 times)
68 uipopulate called (1 times)
69 uipopulate called (1 times)
69 uipopulate called (1 times)
70 uipopulate called (1 times)
70 uipopulate called (1 times)
71 reposetup called for a
71 reposetup called for a
72 ui == repo.ui
72 ui == repo.ui
73 uipopulate called (1 times) (chg !)
73 uipopulate called (1 times) (chg !)
74 uipopulate called (1 times) (chg !)
74 uipopulate called (1 times) (chg !)
75 uipopulate called (1 times) (chg !)
75 uipopulate called (1 times) (chg !)
76 uipopulate called (1 times) (chg !)
76 uipopulate called (1 times) (chg !)
77 uipopulate called (1 times) (chg !)
77 uipopulate called (1 times) (chg !)
78 reposetup called for a (chg !)
78 reposetup called for a (chg !)
79 ui == repo.ui (chg !)
79 ui == repo.ui (chg !)
80 Foo
80 Foo
81 $ hg foo --quiet
81 $ hg foo --quiet
82 uisetup called (no-chg !)
82 uisetup called (no-chg !)
83 uipopulate called (1 times)
83 uipopulate called (1 times)
84 uipopulate called (1 times)
84 uipopulate called (1 times)
85 uipopulate called (1 times) (chg !)
85 uipopulate called (1 times) (chg !)
86 uipopulate called (1 times) (chg !)
86 uipopulate called (1 times) (chg !)
87 uipopulate called (1 times)
87 uipopulate called (1 times)
88 reposetup called for a
88 reposetup called for a
89 ui == repo.ui
89 ui == repo.ui
90 Foo
90 Foo
91 $ hg foo --debug
91 $ hg foo --debug
92 uisetup called [debug] (no-chg !)
92 uisetup called [debug] (no-chg !)
93 uisetup called (no-chg !)
93 uisetup called (no-chg !)
94 uisetup called [status] (no-chg !)
94 uisetup called [status] (no-chg !)
95 uipopulate called (1 times)
95 uipopulate called (1 times)
96 uipopulate called (1 times)
96 uipopulate called (1 times)
97 uipopulate called (1 times) (chg !)
97 uipopulate called (1 times) (chg !)
98 uipopulate called (1 times) (chg !)
98 uipopulate called (1 times) (chg !)
99 uipopulate called (1 times)
99 uipopulate called (1 times)
100 reposetup called for a
100 reposetup called for a
101 ui == repo.ui
101 ui == repo.ui
102 Foo
102 Foo
103
103
104 $ cd ..
104 $ cd ..
105 $ hg clone a b
105 $ hg clone a b
106 uisetup called (no-chg !)
106 uisetup called (no-chg !)
107 uisetup called [status] (no-chg !)
107 uisetup called [status] (no-chg !)
108 uipopulate called (1 times)
108 uipopulate called (1 times)
109 uipopulate called (1 times) (chg !)
109 uipopulate called (1 times) (chg !)
110 uipopulate called (1 times)
110 uipopulate called (1 times)
111 reposetup called for a
111 reposetup called for a
112 ui == repo.ui
112 ui == repo.ui
113 uipopulate called (1 times)
113 uipopulate called (1 times)
114 uipopulate called (1 times)
114 uipopulate called (1 times)
115 reposetup called for b
115 reposetup called for b
116 ui == repo.ui
116 ui == repo.ui
117 updating to branch default
117 updating to branch default
118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119
119
120 $ hg bar
120 $ hg bar
121 uisetup called (no-chg !)
121 uisetup called (no-chg !)
122 uisetup called [status] (no-chg !)
122 uisetup called [status] (no-chg !)
123 uipopulate called (1 times)
123 uipopulate called (1 times)
124 uipopulate called (1 times) (chg !)
124 uipopulate called (1 times) (chg !)
125 Bar
125 Bar
126 $ echo 'foobar = !' >> $HGRCPATH
126 $ echo 'foobar = !' >> $HGRCPATH
127
127
128 module/__init__.py-style
128 module/__init__.py-style
129
129
130 $ echo "barfoo = $barfoopath" >> $HGRCPATH
130 $ echo "barfoo = $barfoopath" >> $HGRCPATH
131 $ cd a
131 $ cd a
132 $ hg foo
132 $ hg foo
133 uisetup called
133 uisetup called
134 uisetup called [status]
134 uisetup called [status]
135 uipopulate called (1 times)
135 uipopulate called (1 times)
136 uipopulate called (1 times)
136 uipopulate called (1 times)
137 uipopulate called (1 times)
137 uipopulate called (1 times)
138 reposetup called for a
138 reposetup called for a
139 ui == repo.ui
139 ui == repo.ui
140 uipopulate called (1 times) (chg !)
140 uipopulate called (1 times) (chg !)
141 uipopulate called (1 times) (chg !)
141 uipopulate called (1 times) (chg !)
142 uipopulate called (1 times) (chg !)
142 uipopulate called (1 times) (chg !)
143 uipopulate called (1 times) (chg !)
143 uipopulate called (1 times) (chg !)
144 uipopulate called (1 times) (chg !)
144 uipopulate called (1 times) (chg !)
145 reposetup called for a (chg !)
145 reposetup called for a (chg !)
146 ui == repo.ui (chg !)
146 ui == repo.ui (chg !)
147 Foo
147 Foo
148 $ echo 'barfoo = !' >> $HGRCPATH
148 $ echo 'barfoo = !' >> $HGRCPATH
149
149
150 Check that extensions are loaded in phases:
150 Check that extensions are loaded in phases:
151
151
152 $ cat > foo.py <<EOF
152 $ cat > foo.py <<EOF
153 > from __future__ import print_function
153 > from __future__ import print_function
154 > import os
154 > import os
155 > from mercurial import exthelper
155 > from mercurial import exthelper
156 > from mercurial.utils import procutil
156 > from mercurial.utils import procutil
157 >
157 >
158 > def write(msg):
158 > def write(msg):
159 > procutil.stdout.write(msg)
159 > procutil.stdout.write(msg)
160 > procutil.stdout.flush()
160 > procutil.stdout.flush()
161 >
161 >
162 > name = os.path.basename(__file__).rsplit('.', 1)[0]
162 > name = os.path.basename(__file__).rsplit('.', 1)[0]
163 > bytesname = name.encode('utf-8')
163 > bytesname = name.encode('utf-8')
164 > write(b"1) %s imported\n" % bytesname)
164 > write(b"1) %s imported\n" % bytesname)
165 > eh = exthelper.exthelper()
165 > eh = exthelper.exthelper()
166 > @eh.uisetup
166 > @eh.uisetup
167 > def _uisetup(ui):
167 > def _uisetup(ui):
168 > write(b"2) %s uisetup\n" % bytesname)
168 > write(b"2) %s uisetup\n" % bytesname)
169 > @eh.extsetup
169 > @eh.extsetup
170 > def _extsetup(ui):
170 > def _extsetup(ui):
171 > write(b"3) %s extsetup\n" % bytesname)
171 > write(b"3) %s extsetup\n" % bytesname)
172 > @eh.uipopulate
172 > @eh.uipopulate
173 > def _uipopulate(ui):
173 > def _uipopulate(ui):
174 > write(b"4) %s uipopulate\n" % bytesname)
174 > write(b"4) %s uipopulate\n" % bytesname)
175 > @eh.reposetup
175 > @eh.reposetup
176 > def _reposetup(ui, repo):
176 > def _reposetup(ui, repo):
177 > write(b"5) %s reposetup\n" % bytesname)
177 > write(b"5) %s reposetup\n" % bytesname)
178 >
178 >
179 > extsetup = eh.finalextsetup
179 > extsetup = eh.finalextsetup
180 > reposetup = eh.finalreposetup
180 > reposetup = eh.finalreposetup
181 > uipopulate = eh.finaluipopulate
181 > uipopulate = eh.finaluipopulate
182 > uisetup = eh.finaluisetup
182 > uisetup = eh.finaluisetup
183 > revsetpredicate = eh.revsetpredicate
183 > revsetpredicate = eh.revsetpredicate
184 >
184 >
185 > # custom predicate to check registration of functions at loading
185 > # custom predicate to check registration of functions at loading
186 > from mercurial import (
186 > from mercurial import (
187 > smartset,
187 > smartset,
188 > )
188 > )
189 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
189 > @eh.revsetpredicate(bytesname, safe=True) # safe=True for query via hgweb
190 > def custompredicate(repo, subset, x):
190 > def custompredicate(repo, subset, x):
191 > return smartset.baseset([r for r in subset if r in {0}])
191 > return smartset.baseset([r for r in subset if r in {0}])
192 > EOF
192 > EOF
193 $ "$PYTHON" $TESTTMP/unflush.py foo.py
193 $ "$PYTHON" $TESTTMP/unflush.py foo.py
194
194
195 $ cp foo.py bar.py
195 $ cp foo.py bar.py
196 $ echo 'foo = foo.py' >> $HGRCPATH
196 $ echo 'foo = foo.py' >> $HGRCPATH
197 $ echo 'bar = bar.py' >> $HGRCPATH
197 $ echo 'bar = bar.py' >> $HGRCPATH
198
198
199 Check normal command's load order of extensions and registration of functions
199 Check normal command's load order of extensions and registration of functions
200
200
201 On chg server, extension should be first set up by the server. Then
201 On chg server, extension should be first set up by the server. Then
202 object-level setup should follow in the worker process.
202 object-level setup should follow in the worker process.
203
203
204 $ hg log -r "foo() and bar()" -q
204 $ hg log -r "foo() and bar()" -q
205 1) foo imported
205 1) foo imported
206 1) bar imported
206 1) bar imported
207 2) foo uisetup
207 2) foo uisetup
208 2) bar uisetup
208 2) bar uisetup
209 3) foo extsetup
209 3) foo extsetup
210 3) bar extsetup
210 3) bar extsetup
211 4) foo uipopulate
211 4) foo uipopulate
212 4) bar uipopulate
212 4) bar uipopulate
213 4) foo uipopulate
213 4) foo uipopulate
214 4) bar uipopulate
214 4) bar uipopulate
215 4) foo uipopulate
215 4) foo uipopulate
216 4) bar uipopulate
216 4) bar uipopulate
217 5) foo reposetup
217 5) foo reposetup
218 5) bar reposetup
218 5) bar reposetup
219 4) foo uipopulate (chg !)
219 4) foo uipopulate (chg !)
220 4) bar uipopulate (chg !)
220 4) bar uipopulate (chg !)
221 4) foo uipopulate (chg !)
221 4) foo uipopulate (chg !)
222 4) bar uipopulate (chg !)
222 4) bar uipopulate (chg !)
223 4) foo uipopulate (chg !)
223 4) foo uipopulate (chg !)
224 4) bar uipopulate (chg !)
224 4) bar uipopulate (chg !)
225 4) foo uipopulate (chg !)
225 4) foo uipopulate (chg !)
226 4) bar uipopulate (chg !)
226 4) bar uipopulate (chg !)
227 4) foo uipopulate (chg !)
227 4) foo uipopulate (chg !)
228 4) bar uipopulate (chg !)
228 4) bar uipopulate (chg !)
229 5) foo reposetup (chg !)
229 5) foo reposetup (chg !)
230 5) bar reposetup (chg !)
230 5) bar reposetup (chg !)
231 0:c24b9ac61126
231 0:c24b9ac61126
232
232
233 Check hgweb's load order of extensions and registration of functions
233 Check hgweb's load order of extensions and registration of functions
234
234
235 $ cat > hgweb.cgi <<EOF
235 $ cat > hgweb.cgi <<EOF
236 > #!$PYTHON
236 > #!$PYTHON
237 > from mercurial import demandimport; demandimport.enable()
237 > from mercurial import demandimport; demandimport.enable()
238 > from mercurial.hgweb import hgweb
238 > from mercurial.hgweb import hgweb
239 > from mercurial.hgweb import wsgicgi
239 > from mercurial.hgweb import wsgicgi
240 > application = hgweb(b'.', b'test repo')
240 > application = hgweb(b'.', b'test repo')
241 > wsgicgi.launch(application)
241 > wsgicgi.launch(application)
242 > EOF
242 > EOF
243 $ . "$TESTDIR/cgienv"
243 $ . "$TESTDIR/cgienv"
244
244
245 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
245 $ PATH_INFO='/' SCRIPT_NAME='' "$PYTHON" hgweb.cgi \
246 > | grep '^[0-9]) ' # ignores HTML output
246 > | grep '^[0-9]) ' # ignores HTML output
247 1) foo imported
247 1) foo imported
248 1) bar imported
248 1) bar imported
249 2) foo uisetup
249 2) foo uisetup
250 2) bar uisetup
250 2) bar uisetup
251 3) foo extsetup
251 3) foo extsetup
252 3) bar extsetup
252 3) bar extsetup
253 4) foo uipopulate
253 4) foo uipopulate
254 4) bar uipopulate
254 4) bar uipopulate
255 4) foo uipopulate
255 4) foo uipopulate
256 4) bar uipopulate
256 4) bar uipopulate
257 5) foo reposetup
257 5) foo reposetup
258 5) bar reposetup
258 5) bar reposetup
259
259
260 (check that revset predicate foo() and bar() are available)
260 (check that revset predicate foo() and bar() are available)
261
261
262 #if msys
262 #if msys
263 $ PATH_INFO='//shortlog'
263 $ PATH_INFO='//shortlog'
264 #else
264 #else
265 $ PATH_INFO='/shortlog'
265 $ PATH_INFO='/shortlog'
266 #endif
266 #endif
267 $ export PATH_INFO
267 $ export PATH_INFO
268 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
268 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' "$PYTHON" hgweb.cgi \
269 > | grep '<a href="/rev/[0-9a-z]*">'
269 > | grep '<a href="/rev/[0-9a-z]*">'
270 <a href="/rev/c24b9ac61126">add file</a>
270 <a href="/rev/c24b9ac61126">add file</a>
271
271
272 $ echo 'foo = !' >> $HGRCPATH
272 $ echo 'foo = !' >> $HGRCPATH
273 $ echo 'bar = !' >> $HGRCPATH
273 $ echo 'bar = !' >> $HGRCPATH
274
274
275 Check "from __future__ import absolute_import" support for external libraries
275 Check "from __future__ import absolute_import" support for external libraries
276
276
277 (import-checker.py reports issues for some of heredoc python code
277 (import-checker.py reports issues for some of heredoc python code
278 fragments below, because import-checker.py does not know test specific
278 fragments below, because import-checker.py does not know test specific
279 package hierarchy. NO_CHECK_* should be used as a limit mark of
279 package hierarchy. NO_CHECK_* should be used as a limit mark of
280 heredoc, in order to make import-checker.py ignore them. For
280 heredoc, in order to make import-checker.py ignore them. For
281 simplicity, all python code fragments below are generated with such
281 simplicity, all python code fragments below are generated with such
282 limit mark, regardless of importing module or not.)
282 limit mark, regardless of importing module or not.)
283
283
284 #if windows
284 #if windows
285 $ PATHSEP=";"
285 $ PATHSEP=";"
286 #else
286 #else
287 $ PATHSEP=":"
287 $ PATHSEP=":"
288 #endif
288 #endif
289 $ export PATHSEP
289 $ export PATHSEP
290
290
291 $ mkdir $TESTTMP/libroot
291 $ mkdir $TESTTMP/libroot
292 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
292 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
293 $ mkdir $TESTTMP/libroot/mod
293 $ mkdir $TESTTMP/libroot/mod
294 $ touch $TESTTMP/libroot/mod/__init__.py
294 $ touch $TESTTMP/libroot/mod/__init__.py
295 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
295 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
296
296
297 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
297 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<NO_CHECK_EOF
298 > from __future__ import absolute_import, print_function
298 > from __future__ import absolute_import, print_function
299 > import ambig # should load "libroot/ambig.py"
299 > import ambig # should load "libroot/ambig.py"
300 > s = ambig.s
300 > s = ambig.s
301 > NO_CHECK_EOF
301 > NO_CHECK_EOF
302 $ cat > loadabs.py <<NO_CHECK_EOF
302 $ cat > loadabs.py <<NO_CHECK_EOF
303 > import mod.ambigabs as ambigabs
303 > import mod.ambigabs as ambigabs
304 > def extsetup(ui):
304 > def extsetup(ui):
305 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
305 > print('ambigabs.s=%s' % ambigabs.s, flush=True)
306 > NO_CHECK_EOF
306 > NO_CHECK_EOF
307 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
307 $ "$PYTHON" $TESTTMP/unflush.py loadabs.py
308 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
308 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
309 ambigabs.s=libroot/ambig.py
309 ambigabs.s=libroot/ambig.py
310 $TESTTMP/a
310 $TESTTMP/a
311
311
312 #if no-py3
312 #if no-py3
313 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
313 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<NO_CHECK_EOF
314 > from __future__ import print_function
314 > from __future__ import print_function
315 > import ambig # should load "libroot/mod/ambig.py"
315 > import ambig # should load "libroot/mod/ambig.py"
316 > s = ambig.s
316 > s = ambig.s
317 > NO_CHECK_EOF
317 > NO_CHECK_EOF
318 $ cat > loadrel.py <<NO_CHECK_EOF
318 $ cat > loadrel.py <<NO_CHECK_EOF
319 > import mod.ambigrel as ambigrel
319 > import mod.ambigrel as ambigrel
320 > def extsetup(ui):
320 > def extsetup(ui):
321 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
321 > print('ambigrel.s=%s' % ambigrel.s, flush=True)
322 > NO_CHECK_EOF
322 > NO_CHECK_EOF
323 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
323 $ "$PYTHON" $TESTTMP/unflush.py loadrel.py
324 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
324 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
325 ambigrel.s=libroot/mod/ambig.py
325 ambigrel.s=libroot/mod/ambig.py
326 $TESTTMP/a
326 $TESTTMP/a
327 #endif
327 #endif
328
328
329 Check absolute/relative import of extension specific modules
329 Check absolute/relative import of extension specific modules
330
330
331 $ mkdir $TESTTMP/extroot
331 $ mkdir $TESTTMP/extroot
332 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
332 $ cat > $TESTTMP/extroot/bar.py <<NO_CHECK_EOF
333 > s = b'this is extroot.bar'
333 > s = b'this is extroot.bar'
334 > NO_CHECK_EOF
334 > NO_CHECK_EOF
335 $ mkdir $TESTTMP/extroot/sub1
335 $ mkdir $TESTTMP/extroot/sub1
336 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
336 $ cat > $TESTTMP/extroot/sub1/__init__.py <<NO_CHECK_EOF
337 > s = b'this is extroot.sub1.__init__'
337 > s = b'this is extroot.sub1.__init__'
338 > NO_CHECK_EOF
338 > NO_CHECK_EOF
339 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
339 $ cat > $TESTTMP/extroot/sub1/baz.py <<NO_CHECK_EOF
340 > s = b'this is extroot.sub1.baz'
340 > s = b'this is extroot.sub1.baz'
341 > NO_CHECK_EOF
341 > NO_CHECK_EOF
342 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
342 $ cat > $TESTTMP/extroot/__init__.py <<NO_CHECK_EOF
343 > from __future__ import absolute_import
343 > from __future__ import absolute_import
344 > s = b'this is extroot.__init__'
344 > s = b'this is extroot.__init__'
345 > from . import foo
345 > from . import foo
346 > def extsetup(ui):
346 > def extsetup(ui):
347 > ui.write(b'(extroot) ', foo.func(), b'\n')
347 > ui.write(b'(extroot) ', foo.func(), b'\n')
348 > ui.flush()
348 > ui.flush()
349 > NO_CHECK_EOF
349 > NO_CHECK_EOF
350
350
351 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
351 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
352 > # test absolute import
352 > # test absolute import
353 > buf = []
353 > buf = []
354 > def func():
354 > def func():
355 > # "not locals" case
355 > # "not locals" case
356 > import extroot.bar
356 > import extroot.bar
357 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
357 > buf.append(b'import extroot.bar in func(): %s' % extroot.bar.s)
358 > return b'\n(extroot) '.join(buf)
358 > return b'\n(extroot) '.join(buf)
359 > # b"fromlist == ('*',)" case
359 > # b"fromlist == ('*',)" case
360 > from extroot.bar import *
360 > from extroot.bar import *
361 > buf.append(b'from extroot.bar import *: %s' % s)
361 > buf.append(b'from extroot.bar import *: %s' % s)
362 > # "not fromlist" and "if '.' in name" case
362 > # "not fromlist" and "if '.' in name" case
363 > import extroot.sub1.baz
363 > import extroot.sub1.baz
364 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
364 > buf.append(b'import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
365 > # "not fromlist" and NOT "if '.' in name" case
365 > # "not fromlist" and NOT "if '.' in name" case
366 > import extroot
366 > import extroot
367 > buf.append(b'import extroot: %s' % extroot.s)
367 > buf.append(b'import extroot: %s' % extroot.s)
368 > # NOT "not fromlist" and NOT "level != -1" case
368 > # NOT "not fromlist" and NOT "level != -1" case
369 > from extroot.bar import s
369 > from extroot.bar import s
370 > buf.append(b'from extroot.bar import s: %s' % s)
370 > buf.append(b'from extroot.bar import s: %s' % s)
371 > NO_CHECK_EOF
371 > NO_CHECK_EOF
372 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
372 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
373 (extroot) from extroot.bar import *: this is extroot.bar
373 (extroot) from extroot.bar import *: this is extroot.bar
374 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
374 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
375 (extroot) import extroot: this is extroot.__init__
375 (extroot) import extroot: this is extroot.__init__
376 (extroot) from extroot.bar import s: this is extroot.bar
376 (extroot) from extroot.bar import s: this is extroot.bar
377 (extroot) import extroot.bar in func(): this is extroot.bar
377 (extroot) import extroot.bar in func(): this is extroot.bar
378 $TESTTMP/a
378 $TESTTMP/a
379
379
380 #if no-py3
380 #if no-py3
381 $ rm "$TESTTMP"/extroot/foo.*
381 $ rm "$TESTTMP"/extroot/foo.*
382 $ rm -Rf "$TESTTMP/extroot/__pycache__"
382 $ rm -Rf "$TESTTMP/extroot/__pycache__"
383 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
383 $ cat > $TESTTMP/extroot/foo.py <<NO_CHECK_EOF
384 > # test relative import
384 > # test relative import
385 > buf = []
385 > buf = []
386 > def func():
386 > def func():
387 > # "not locals" case
387 > # "not locals" case
388 > import bar
388 > import bar
389 > buf.append('import bar in func(): %s' % bar.s)
389 > buf.append('import bar in func(): %s' % bar.s)
390 > return '\n(extroot) '.join(buf)
390 > return '\n(extroot) '.join(buf)
391 > # "fromlist == ('*',)" case
391 > # "fromlist == ('*',)" case
392 > from bar import *
392 > from bar import *
393 > buf.append('from bar import *: %s' % s)
393 > buf.append('from bar import *: %s' % s)
394 > # "not fromlist" and "if '.' in name" case
394 > # "not fromlist" and "if '.' in name" case
395 > import sub1.baz
395 > import sub1.baz
396 > buf.append('import sub1.baz: %s' % sub1.baz.s)
396 > buf.append('import sub1.baz: %s' % sub1.baz.s)
397 > # "not fromlist" and NOT "if '.' in name" case
397 > # "not fromlist" and NOT "if '.' in name" case
398 > import sub1
398 > import sub1
399 > buf.append('import sub1: %s' % sub1.s)
399 > buf.append('import sub1: %s' % sub1.s)
400 > # NOT "not fromlist" and NOT "level != -1" case
400 > # NOT "not fromlist" and NOT "level != -1" case
401 > from bar import s
401 > from bar import s
402 > buf.append('from bar import s: %s' % s)
402 > buf.append('from bar import s: %s' % s)
403 > NO_CHECK_EOF
403 > NO_CHECK_EOF
404 $ hg --config extensions.extroot=$TESTTMP/extroot root
404 $ hg --config extensions.extroot=$TESTTMP/extroot root
405 (extroot) from bar import *: this is extroot.bar
405 (extroot) from bar import *: this is extroot.bar
406 (extroot) import sub1.baz: this is extroot.sub1.baz
406 (extroot) import sub1.baz: this is extroot.sub1.baz
407 (extroot) import sub1: this is extroot.sub1.__init__
407 (extroot) import sub1: this is extroot.sub1.__init__
408 (extroot) from bar import s: this is extroot.bar
408 (extroot) from bar import s: this is extroot.bar
409 (extroot) import bar in func(): this is extroot.bar
409 (extroot) import bar in func(): this is extroot.bar
410 $TESTTMP/a
410 $TESTTMP/a
411 #endif
411 #endif
412
412
413 #if demandimport
413 #if demandimport
414
414
415 Examine whether module loading is delayed until actual referring, even
415 Examine whether module loading is delayed until actual referring, even
416 though module is imported with "absolute_import" feature.
416 though module is imported with "absolute_import" feature.
417
417
418 Files below in each packages are used for described purpose:
418 Files below in each packages are used for described purpose:
419
419
420 - "called": examine whether "from MODULE import ATTR" works correctly
420 - "called": examine whether "from MODULE import ATTR" works correctly
421 - "unused": examine whether loading is delayed correctly
421 - "unused": examine whether loading is delayed correctly
422 - "used": examine whether "from PACKAGE import MODULE" works correctly
422 - "used": examine whether "from PACKAGE import MODULE" works correctly
423
423
424 Package hierarchy is needed to examine whether demand importing works
424 Package hierarchy is needed to examine whether demand importing works
425 as expected for "from SUB.PACK.AGE import MODULE".
425 as expected for "from SUB.PACK.AGE import MODULE".
426
426
427 Setup "external library" to be imported with "absolute_import"
427 Setup "external library" to be imported with "absolute_import"
428 feature.
428 feature.
429
429
430 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
430 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
431 $ touch $TESTTMP/extlibroot/__init__.py
431 $ touch $TESTTMP/extlibroot/__init__.py
432 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
432 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
433 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
433 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
434
434
435 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
435 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<NO_CHECK_EOF
436 > def func():
436 > def func():
437 > return b"this is extlibroot.lsub1.lsub2.called.func()"
437 > return b"this is extlibroot.lsub1.lsub2.called.func()"
438 > NO_CHECK_EOF
438 > NO_CHECK_EOF
439 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
439 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<NO_CHECK_EOF
440 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
440 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
441 > NO_CHECK_EOF
441 > NO_CHECK_EOF
442 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
442 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<NO_CHECK_EOF
443 > detail = b"this is extlibroot.lsub1.lsub2.used"
443 > detail = b"this is extlibroot.lsub1.lsub2.used"
444 > NO_CHECK_EOF
444 > NO_CHECK_EOF
445
445
446 Setup sub-package of "external library", which causes instantiation of
446 Setup sub-package of "external library", which causes instantiation of
447 demandmod in "recurse down the module chain" code path. Relative
447 demandmod in "recurse down the module chain" code path. Relative
448 importing with "absolute_import" feature isn't tested, because "level
448 importing with "absolute_import" feature isn't tested, because "level
449 >=1 " doesn't cause instantiation of demandmod.
449 >=1 " doesn't cause instantiation of demandmod.
450
450
451 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
451 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
452 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
452 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<NO_CHECK_EOF
453 > detail = b"this is extlibroot.recursedown.abs.used"
453 > detail = b"this is extlibroot.recursedown.abs.used"
454 > NO_CHECK_EOF
454 > NO_CHECK_EOF
455 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
455 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<NO_CHECK_EOF
456 > from __future__ import absolute_import
456 > from __future__ import absolute_import
457 > from extlibroot.recursedown.abs.used import detail
457 > from extlibroot.recursedown.abs.used import detail
458 > NO_CHECK_EOF
458 > NO_CHECK_EOF
459
459
460 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
460 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
461 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
461 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<NO_CHECK_EOF
462 > detail = b"this is extlibroot.recursedown.legacy.used"
462 > detail = b"this is extlibroot.recursedown.legacy.used"
463 > NO_CHECK_EOF
463 > NO_CHECK_EOF
464 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
464 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<NO_CHECK_EOF
465 > # legacy style (level == -1) import
465 > # legacy style (level == -1) import
466 > from extlibroot.recursedown.legacy.used import detail
466 > from extlibroot.recursedown.legacy.used import detail
467 > NO_CHECK_EOF
467 > NO_CHECK_EOF
468
468
469 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
469 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<NO_CHECK_EOF
470 > from __future__ import absolute_import
470 > from __future__ import absolute_import
471 > from extlibroot.recursedown.abs import detail as absdetail
471 > from extlibroot.recursedown.abs import detail as absdetail
472 > from .legacy import detail as legacydetail
472 > from .legacy import detail as legacydetail
473 > NO_CHECK_EOF
473 > NO_CHECK_EOF
474
474
475 Setup package that re-exports an attribute of its submodule as the same
475 Setup package that re-exports an attribute of its submodule as the same
476 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
476 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
477 the submodule 'used' should be somehow accessible. (issue5617)
477 the submodule 'used' should be somehow accessible. (issue5617)
478
478
479 $ mkdir -p $TESTTMP/extlibroot/shadowing
479 $ mkdir -p $TESTTMP/extlibroot/shadowing
480 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
480 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<NO_CHECK_EOF
481 > detail = b"this is extlibroot.shadowing.used"
481 > detail = b"this is extlibroot.shadowing.used"
482 > NO_CHECK_EOF
482 > NO_CHECK_EOF
483 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
483 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<NO_CHECK_EOF
484 > from __future__ import absolute_import
484 > from __future__ import absolute_import
485 > from extlibroot.shadowing.used import detail
485 > from extlibroot.shadowing.used import detail
486 > NO_CHECK_EOF
486 > NO_CHECK_EOF
487 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
487 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<NO_CHECK_EOF
488 > from __future__ import absolute_import
488 > from __future__ import absolute_import
489 > from .used import detail as used
489 > from .used import detail as used
490 > NO_CHECK_EOF
490 > NO_CHECK_EOF
491
491
492 Setup extension local modules to be imported with "absolute_import"
492 Setup extension local modules to be imported with "absolute_import"
493 feature.
493 feature.
494
494
495 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
495 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
496 $ touch $TESTTMP/absextroot/xsub1/__init__.py
496 $ touch $TESTTMP/absextroot/xsub1/__init__.py
497 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
497 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
498
498
499 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
499 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<NO_CHECK_EOF
500 > def func():
500 > def func():
501 > return b"this is absextroot.xsub1.xsub2.called.func()"
501 > return b"this is absextroot.xsub1.xsub2.called.func()"
502 > NO_CHECK_EOF
502 > NO_CHECK_EOF
503 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
503 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<NO_CHECK_EOF
504 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
504 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
505 > NO_CHECK_EOF
505 > NO_CHECK_EOF
506 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
506 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<NO_CHECK_EOF
507 > detail = b"this is absextroot.xsub1.xsub2.used"
507 > detail = b"this is absextroot.xsub1.xsub2.used"
508 > NO_CHECK_EOF
508 > NO_CHECK_EOF
509
509
510 Setup extension local modules to examine whether demand importing
510 Setup extension local modules to examine whether demand importing
511 works as expected in "level > 1" case.
511 works as expected in "level > 1" case.
512
512
513 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
513 $ cat > $TESTTMP/absextroot/relimportee.py <<NO_CHECK_EOF
514 > detail = b"this is absextroot.relimportee"
514 > detail = b"this is absextroot.relimportee"
515 > NO_CHECK_EOF
515 > NO_CHECK_EOF
516 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
516 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<NO_CHECK_EOF
517 > from __future__ import absolute_import
517 > from __future__ import absolute_import
518 > from mercurial import pycompat
518 > from mercurial import pycompat
519 > from ... import relimportee
519 > from ... import relimportee
520 > detail = b"this relimporter imports %r" % (
520 > detail = b"this relimporter imports %r" % (
521 > pycompat.bytestr(relimportee.detail))
521 > pycompat.bytestr(relimportee.detail))
522 > NO_CHECK_EOF
522 > NO_CHECK_EOF
523
523
524 Setup modules, which actually import extension local modules at
524 Setup modules, which actually import extension local modules at
525 runtime.
525 runtime.
526
526
527 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
527 $ cat > $TESTTMP/absextroot/absolute.py << NO_CHECK_EOF
528 > from __future__ import absolute_import
528 > from __future__ import absolute_import
529 >
529 >
530 > # import extension local modules absolutely (level = 0)
530 > # import extension local modules absolutely (level = 0)
531 > from absextroot.xsub1.xsub2 import used, unused
531 > from absextroot.xsub1.xsub2 import used, unused
532 > from absextroot.xsub1.xsub2.called import func
532 > from absextroot.xsub1.xsub2.called import func
533 >
533 >
534 > def getresult():
534 > def getresult():
535 > result = []
535 > result = []
536 > result.append(used.detail)
536 > result.append(used.detail)
537 > result.append(func())
537 > result.append(func())
538 > return result
538 > return result
539 > NO_CHECK_EOF
539 > NO_CHECK_EOF
540
540
541 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
541 $ cat > $TESTTMP/absextroot/relative.py << NO_CHECK_EOF
542 > from __future__ import absolute_import
542 > from __future__ import absolute_import
543 >
543 >
544 > # import extension local modules relatively (level == 1)
544 > # import extension local modules relatively (level == 1)
545 > from .xsub1.xsub2 import used, unused
545 > from .xsub1.xsub2 import used, unused
546 > from .xsub1.xsub2.called import func
546 > from .xsub1.xsub2.called import func
547 >
547 >
548 > # import a module, which implies "importing with level > 1"
548 > # import a module, which implies "importing with level > 1"
549 > from .xsub1.xsub2 import relimporter
549 > from .xsub1.xsub2 import relimporter
550 >
550 >
551 > def getresult():
551 > def getresult():
552 > result = []
552 > result = []
553 > result.append(used.detail)
553 > result.append(used.detail)
554 > result.append(func())
554 > result.append(func())
555 > result.append(relimporter.detail)
555 > result.append(relimporter.detail)
556 > return result
556 > return result
557 > NO_CHECK_EOF
557 > NO_CHECK_EOF
558
558
559 Setup main procedure of extension.
559 Setup main procedure of extension.
560
560
561 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
561 $ cat > $TESTTMP/absextroot/__init__.py <<NO_CHECK_EOF
562 > from __future__ import absolute_import
562 > from __future__ import absolute_import
563 > from mercurial import registrar
563 > from mercurial import registrar
564 > cmdtable = {}
564 > cmdtable = {}
565 > command = registrar.command(cmdtable)
565 > command = registrar.command(cmdtable)
566 >
566 >
567 > # "absolute" and "relative" shouldn't be imported before actual
567 > # "absolute" and "relative" shouldn't be imported before actual
568 > # command execution, because (1) they import same modules, and (2)
568 > # command execution, because (1) they import same modules, and (2)
569 > # preceding import (= instantiate "demandmod" object instead of
569 > # preceding import (= instantiate "demandmod" object instead of
570 > # real "module" object) might hide problem of succeeding import.
570 > # real "module" object) might hide problem of succeeding import.
571 >
571 >
572 > @command(b'showabsolute', [], norepo=True)
572 > @command(b'showabsolute', [], norepo=True)
573 > def showabsolute(ui, *args, **opts):
573 > def showabsolute(ui, *args, **opts):
574 > from absextroot import absolute
574 > from absextroot import absolute
575 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
575 > ui.write(b'ABS: %s\n' % b'\nABS: '.join(absolute.getresult()))
576 >
576 >
577 > @command(b'showrelative', [], norepo=True)
577 > @command(b'showrelative', [], norepo=True)
578 > def showrelative(ui, *args, **opts):
578 > def showrelative(ui, *args, **opts):
579 > from . import relative
579 > from . import relative
580 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
580 > ui.write(b'REL: %s\n' % b'\nREL: '.join(relative.getresult()))
581 >
581 >
582 > # import modules from external library
582 > # import modules from external library
583 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
583 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
584 > from extlibroot.lsub1.lsub2.called import func as lfunc
584 > from extlibroot.lsub1.lsub2.called import func as lfunc
585 > from extlibroot.recursedown import absdetail, legacydetail
585 > from extlibroot.recursedown import absdetail, legacydetail
586 > from extlibroot.shadowing import proxied
586 > from extlibroot.shadowing import proxied
587 >
587 >
588 > def uisetup(ui):
588 > def uisetup(ui):
589 > result = []
589 > result = []
590 > result.append(lused.detail)
590 > result.append(lused.detail)
591 > result.append(lfunc())
591 > result.append(lfunc())
592 > result.append(absdetail)
592 > result.append(absdetail)
593 > result.append(legacydetail)
593 > result.append(legacydetail)
594 > result.append(proxied.detail)
594 > result.append(proxied.detail)
595 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
595 > ui.write(b'LIB: %s\n' % b'\nLIB: '.join(result))
596 > NO_CHECK_EOF
596 > NO_CHECK_EOF
597
597
598 Examine module importing.
598 Examine module importing.
599
599
600 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
600 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
601 LIB: this is extlibroot.lsub1.lsub2.used
601 LIB: this is extlibroot.lsub1.lsub2.used
602 LIB: this is extlibroot.lsub1.lsub2.called.func()
602 LIB: this is extlibroot.lsub1.lsub2.called.func()
603 LIB: this is extlibroot.recursedown.abs.used
603 LIB: this is extlibroot.recursedown.abs.used
604 LIB: this is extlibroot.recursedown.legacy.used
604 LIB: this is extlibroot.recursedown.legacy.used
605 LIB: this is extlibroot.shadowing.used
605 LIB: this is extlibroot.shadowing.used
606 ABS: this is absextroot.xsub1.xsub2.used
606 ABS: this is absextroot.xsub1.xsub2.used
607 ABS: this is absextroot.xsub1.xsub2.called.func()
607 ABS: this is absextroot.xsub1.xsub2.called.func()
608
608
609 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
609 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
610 LIB: this is extlibroot.lsub1.lsub2.used
610 LIB: this is extlibroot.lsub1.lsub2.used
611 LIB: this is extlibroot.lsub1.lsub2.called.func()
611 LIB: this is extlibroot.lsub1.lsub2.called.func()
612 LIB: this is extlibroot.recursedown.abs.used
612 LIB: this is extlibroot.recursedown.abs.used
613 LIB: this is extlibroot.recursedown.legacy.used
613 LIB: this is extlibroot.recursedown.legacy.used
614 LIB: this is extlibroot.shadowing.used
614 LIB: this is extlibroot.shadowing.used
615 REL: this is absextroot.xsub1.xsub2.used
615 REL: this is absextroot.xsub1.xsub2.used
616 REL: this is absextroot.xsub1.xsub2.called.func()
616 REL: this is absextroot.xsub1.xsub2.called.func()
617 REL: this relimporter imports 'this is absextroot.relimportee'
617 REL: this relimporter imports 'this is absextroot.relimportee'
618
618
619 Examine whether sub-module is imported relatively as expected.
619 Examine whether sub-module is imported relatively as expected.
620
620
621 See also issue5208 for detail about example case on Python 3.x.
621 See also issue5208 for detail about example case on Python 3.x.
622
622
623 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
623 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
624 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
624 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
625
625
626 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
626 $ cat > $TESTTMP/notexist.py <<NO_CHECK_EOF
627 > text = 'notexist.py at root is loaded unintentionally\n'
627 > text = 'notexist.py at root is loaded unintentionally\n'
628 > NO_CHECK_EOF
628 > NO_CHECK_EOF
629
629
630 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
630 $ cat > $TESTTMP/checkrelativity.py <<NO_CHECK_EOF
631 > from mercurial import registrar
631 > from mercurial import registrar
632 > cmdtable = {}
632 > cmdtable = {}
633 > command = registrar.command(cmdtable)
633 > command = registrar.command(cmdtable)
634 >
634 >
635 > # demand import avoids failure of importing notexist here, but only on
635 > # demand import avoids failure of importing notexist here, but only on
636 > # Python 2.
636 > # Python 2.
637 > import extlibroot.lsub1.lsub2.notexist
637 > import extlibroot.lsub1.lsub2.notexist
638 >
638 >
639 > @command(b'checkrelativity', [], norepo=True)
639 > @command(b'checkrelativity', [], norepo=True)
640 > def checkrelativity(ui, *args, **opts):
640 > def checkrelativity(ui, *args, **opts):
641 > try:
641 > try:
642 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
642 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
643 > return 1 # unintentional success
643 > return 1 # unintentional success
644 > except ImportError:
644 > except ImportError:
645 > pass # intentional failure
645 > pass # intentional failure
646 > NO_CHECK_EOF
646 > NO_CHECK_EOF
647
647
648 Python 3's lazy importer verifies modules exist before returning the lazy
648 Python 3's lazy importer verifies modules exist before returning the lazy
649 module stub. Our custom lazy importer for Python 2 always returns a stub.
649 module stub. Our custom lazy importer for Python 2 always returns a stub.
650
650
651 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) || true
651 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) || true
652 *** failed to import extension checkrelativity from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist' (py3 !)
652 *** failed to import extension checkrelativity from $TESTTMP/checkrelativity.py: No module named 'extlibroot.lsub1.lsub2.notexist' (py3 !)
653 hg: unknown command 'checkrelativity' (py3 !)
653 hg: unknown command 'checkrelativity' (py3 !)
654 (use 'hg help' for a list of commands) (py3 !)
654 (use 'hg help' for a list of commands) (py3 !)
655
655
656 #endif
656 #endif
657
657
658 (Here, module importing tests are finished. Therefore, use other than
658 (Here, module importing tests are finished. Therefore, use other than
659 NO_CHECK_* limit mark for heredoc python files, in order to apply
659 NO_CHECK_* limit mark for heredoc python files, in order to apply
660 import-checker.py or so on their contents)
660 import-checker.py or so on their contents)
661
661
662 Make sure a broken uisetup doesn't globally break hg:
662 Make sure a broken uisetup doesn't globally break hg:
663 $ cat > $TESTTMP/baduisetup.py <<EOF
663 $ cat > $TESTTMP/baduisetup.py <<EOF
664 > def uisetup(ui):
664 > def uisetup(ui):
665 > 1 / 0
665 > 1 / 0
666 > EOF
666 > EOF
667
667
668 Even though the extension fails during uisetup, hg is still basically usable:
668 Even though the extension fails during uisetup, hg is still basically usable:
669 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
669 $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
670 Traceback (most recent call last):
670 Traceback (most recent call last):
671 File "*/mercurial/extensions.py", line *, in _runuisetup (glob) (no-pyoxidizer !)
671 File "*/mercurial/extensions.py", line *, in _runuisetup (glob) (no-pyoxidizer !)
672 File "mercurial.extensions", line *, in _runuisetup (glob) (pyoxidizer !)
672 File "mercurial.extensions", line *, in _runuisetup (glob) (pyoxidizer !)
673 uisetup(ui)
673 uisetup(ui)
674 File "$TESTTMP/baduisetup.py", line 2, in uisetup
674 File "$TESTTMP/baduisetup.py", line 2, in uisetup
675 1 / 0
675 1 / 0
676 ZeroDivisionError: * by zero (glob)
676 ZeroDivisionError: * by zero (glob)
677 *** failed to set up extension baduisetup: * by zero (glob)
677 *** failed to set up extension baduisetup: * by zero (glob)
678 Mercurial Distributed SCM (version *) (glob)
678 Mercurial Distributed SCM (version *) (glob)
679 (see https://mercurial-scm.org for more information)
679 (see https://mercurial-scm.org for more information)
680
680
681 Copyright (C) 2005-* Olivia Mackall and others (glob)
681 Copyright (C) 2005-* Olivia Mackall and others (glob)
682 This is free software; see the source for copying conditions. There is NO
682 This is free software; see the source for copying conditions. There is NO
683 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
683 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
684
684
685 $ cd ..
685 $ cd ..
686
686
687 hide outer repo
687 hide outer repo
688 $ hg init
688 $ hg init
689
689
690 $ cat > empty.py <<EOF
690 $ cat > empty.py <<EOF
691 > '''empty cmdtable
691 > '''empty cmdtable
692 > '''
692 > '''
693 > cmdtable = {}
693 > cmdtable = {}
694 > EOF
694 > EOF
695 $ emptypath=`pwd`/empty.py
695 $ emptypath=`pwd`/empty.py
696 $ echo "empty = $emptypath" >> $HGRCPATH
696 $ echo "empty = $emptypath" >> $HGRCPATH
697 $ hg help empty
697 $ hg help empty
698 empty extension - empty cmdtable
698 empty extension - empty cmdtable
699
699
700 no commands defined
700 no commands defined
701
701
702
702
703 $ echo 'empty = !' >> $HGRCPATH
703 $ echo 'empty = !' >> $HGRCPATH
704
704
705 $ cat > debugextension.py <<EOF
705 $ cat > debugextension.py <<EOF
706 > '''only debugcommands
706 > '''only debugcommands
707 > '''
707 > '''
708 > from mercurial import registrar
708 > from mercurial import registrar
709 > cmdtable = {}
709 > cmdtable = {}
710 > command = registrar.command(cmdtable)
710 > command = registrar.command(cmdtable)
711 > @command(b'debugfoobar', [], b'hg debugfoobar')
711 > @command(b'debugfoobar', [], b'hg debugfoobar')
712 > def debugfoobar(ui, repo, *args, **opts):
712 > def debugfoobar(ui, repo, *args, **opts):
713 > "yet another debug command"
713 > "yet another debug command"
714 > @command(b'foo', [], b'hg foo')
714 > @command(b'foo', [], b'hg foo')
715 > def foo(ui, repo, *args, **opts):
715 > def foo(ui, repo, *args, **opts):
716 > """yet another foo command
716 > """yet another foo command
717 > This command has been DEPRECATED since forever.
717 > This command has been DEPRECATED since forever.
718 > """
718 > """
719 > EOF
719 > EOF
720 $ debugpath=`pwd`/debugextension.py
720 $ debugpath=`pwd`/debugextension.py
721 $ echo "debugextension = $debugpath" >> $HGRCPATH
721 $ echo "debugextension = $debugpath" >> $HGRCPATH
722
722
723 $ hg help debugextension
723 $ hg help debugextension
724 hg debugextensions
724 hg debugextensions
725
725
726 show information about active extensions
726 show information about active extensions
727
727
728 options:
728 options:
729
729
730 -T --template TEMPLATE display with template
730 -T --template TEMPLATE display with template
731
731
732 (some details hidden, use --verbose to show complete help)
732 (some details hidden, use --verbose to show complete help)
733
733
734
734
735 $ hg --verbose help debugextension
735 $ hg --verbose help debugextension
736 hg debugextensions
736 hg debugextensions
737
737
738 show information about active extensions
738 show information about active extensions
739
739
740 options:
740 options:
741
741
742 -T --template TEMPLATE display with template
742 -T --template TEMPLATE display with template
743
743
744 global options ([+] can be repeated):
744 global options ([+] can be repeated):
745
745
746 -R --repository REPO repository root directory or name of overlay bundle
746 -R --repository REPO repository root directory or name of overlay bundle
747 file
747 file
748 --cwd DIR change working directory
748 --cwd DIR change working directory
749 -y --noninteractive do not prompt, automatically pick the first choice for
749 -y --noninteractive do not prompt, automatically pick the first choice for
750 all prompts
750 all prompts
751 -q --quiet suppress output
751 -q --quiet suppress output
752 -v --verbose enable additional output
752 -v --verbose enable additional output
753 --color TYPE when to colorize (boolean, always, auto, never, or
753 --color TYPE when to colorize (boolean, always, auto, never, or
754 debug)
754 debug)
755 --config CONFIG [+] set/override config option (use 'section.name=value')
755 --config CONFIG [+] set/override config option (use 'section.name=value')
756 --debug enable debugging output
756 --debug enable debugging output
757 --debugger start debugger
757 --debugger start debugger
758 --encoding ENCODE set the charset encoding (default: ascii)
758 --encoding ENCODE set the charset encoding (default: ascii)
759 --encodingmode MODE set the charset encoding mode (default: strict)
759 --encodingmode MODE set the charset encoding mode (default: strict)
760 --traceback always print a traceback on exception
760 --traceback always print a traceback on exception
761 --time time how long the command takes
761 --time time how long the command takes
762 --profile print command execution profile
762 --profile print command execution profile
763 --version output version information and exit
763 --version output version information and exit
764 -h --help display help and exit
764 -h --help display help and exit
765 --hidden consider hidden changesets
765 --hidden consider hidden changesets
766 --pager TYPE when to paginate (boolean, always, auto, or never)
766 --pager TYPE when to paginate (boolean, always, auto, or never)
767 (default: auto)
767 (default: auto)
768
768
769
769
770
770
771
771
772
772
773
773
774 $ hg --debug help debugextension
774 $ hg --debug help debugextension
775 hg debugextensions
775 hg debugextensions
776
776
777 show information about active extensions
777 show information about active extensions
778
778
779 options:
779 options:
780
780
781 -T --template TEMPLATE display with template
781 -T --template TEMPLATE display with template
782
782
783 global options ([+] can be repeated):
783 global options ([+] can be repeated):
784
784
785 -R --repository REPO repository root directory or name of overlay bundle
785 -R --repository REPO repository root directory or name of overlay bundle
786 file
786 file
787 --cwd DIR change working directory
787 --cwd DIR change working directory
788 -y --noninteractive do not prompt, automatically pick the first choice for
788 -y --noninteractive do not prompt, automatically pick the first choice for
789 all prompts
789 all prompts
790 -q --quiet suppress output
790 -q --quiet suppress output
791 -v --verbose enable additional output
791 -v --verbose enable additional output
792 --color TYPE when to colorize (boolean, always, auto, never, or
792 --color TYPE when to colorize (boolean, always, auto, never, or
793 debug)
793 debug)
794 --config CONFIG [+] set/override config option (use 'section.name=value')
794 --config CONFIG [+] set/override config option (use 'section.name=value')
795 --debug enable debugging output
795 --debug enable debugging output
796 --debugger start debugger
796 --debugger start debugger
797 --encoding ENCODE set the charset encoding (default: ascii)
797 --encoding ENCODE set the charset encoding (default: ascii)
798 --encodingmode MODE set the charset encoding mode (default: strict)
798 --encodingmode MODE set the charset encoding mode (default: strict)
799 --traceback always print a traceback on exception
799 --traceback always print a traceback on exception
800 --time time how long the command takes
800 --time time how long the command takes
801 --profile print command execution profile
801 --profile print command execution profile
802 --version output version information and exit
802 --version output version information and exit
803 -h --help display help and exit
803 -h --help display help and exit
804 --hidden consider hidden changesets
804 --hidden consider hidden changesets
805 --pager TYPE when to paginate (boolean, always, auto, or never)
805 --pager TYPE when to paginate (boolean, always, auto, or never)
806 (default: auto)
806 (default: auto)
807
807
808
808
809
809
810
810
811
811
812 $ echo 'debugextension = !' >> $HGRCPATH
812 $ echo 'debugextension = !' >> $HGRCPATH
813
813
814 Asking for help about a deprecated extension should do something useful:
814 Asking for help about a deprecated extension should do something useful:
815
815
816 $ hg help glog
816 $ hg help glog
817 'glog' is provided by the following extension:
817 'glog' is provided by the following extension:
818
818
819 graphlog command to view revision graphs from a shell (DEPRECATED)
819 graphlog command to view revision graphs from a shell (DEPRECATED)
820
820
821 (use 'hg help extensions' for information on enabling extensions)
821 (use 'hg help extensions' for information on enabling extensions)
822
822
823 Extension module help vs command help:
823 Extension module help vs command help:
824
824
825 $ echo 'extdiff =' >> $HGRCPATH
825 $ echo 'extdiff =' >> $HGRCPATH
826 $ hg help extdiff
826 $ hg help extdiff
827 hg extdiff [OPT]... [FILE]...
827 hg extdiff [OPT]... [FILE]...
828
828
829 use external program to diff repository (or selected files)
829 use external program to diff repository (or selected files)
830
830
831 Show differences between revisions for the specified files, using an
831 Show differences between revisions for the specified files, using an
832 external program. The default program used is diff, with default options
832 external program. The default program used is diff, with default options
833 "-Npru".
833 "-Npru".
834
834
835 To select a different program, use the -p/--program option. The program
835 To select a different program, use the -p/--program option. The program
836 will be passed the names of two directories to compare, unless the --per-
836 will be passed the names of two directories to compare, unless the --per-
837 file option is specified (see below). To pass additional options to the
837 file option is specified (see below). To pass additional options to the
838 program, use -o/--option. These will be passed before the names of the
838 program, use -o/--option. These will be passed before the names of the
839 directories or files to compare.
839 directories or files to compare.
840
840
841 The --from, --to, and --change options work the same way they do for 'hg
841 The --from, --to, and --change options work the same way they do for 'hg
842 diff'.
842 diff'.
843
843
844 The --per-file option runs the external program repeatedly on each file to
844 The --per-file option runs the external program repeatedly on each file to
845 diff, instead of once on two directories. By default, this happens one by
845 diff, instead of once on two directories. By default, this happens one by
846 one, where the next file diff is open in the external program only once
846 one, where the next file diff is open in the external program only once
847 the previous external program (for the previous file diff) has exited. If
847 the previous external program (for the previous file diff) has exited. If
848 the external program has a graphical interface, it can open all the file
848 the external program has a graphical interface, it can open all the file
849 diffs at once instead of one by one. See 'hg help -e extdiff' for
849 diffs at once instead of one by one. See 'hg help -e extdiff' for
850 information about how to tell Mercurial that a given program has a
850 information about how to tell Mercurial that a given program has a
851 graphical interface.
851 graphical interface.
852
852
853 The --confirm option will prompt the user before each invocation of the
853 The --confirm option will prompt the user before each invocation of the
854 external program. It is ignored if --per-file isn't specified.
854 external program. It is ignored if --per-file isn't specified.
855
855
856 (use 'hg help -e extdiff' to show help for the extdiff extension)
856 (use 'hg help -e extdiff' to show help for the extdiff extension)
857
857
858 options ([+] can be repeated):
858 options ([+] can be repeated):
859
859
860 -p --program CMD comparison program to run
860 -p --program CMD comparison program to run
861 -o --option OPT [+] pass option to comparison program
861 -o --option OPT [+] pass option to comparison program
862 --from REV1 revision to diff from
862 --from REV1 revision to diff from
863 --to REV2 revision to diff to
863 --to REV2 revision to diff to
864 -c --change REV change made by revision
864 -c --change REV change made by revision
865 --per-file compare each file instead of revision snapshots
865 --per-file compare each file instead of revision snapshots
866 --confirm prompt user before each external program invocation
866 --confirm prompt user before each external program invocation
867 --patch compare patches for two revisions
867 --patch compare patches for two revisions
868 -I --include PATTERN [+] include names matching the given patterns
868 -I --include PATTERN [+] include names matching the given patterns
869 -X --exclude PATTERN [+] exclude names matching the given patterns
869 -X --exclude PATTERN [+] exclude names matching the given patterns
870 -S --subrepos recurse into subrepositories
870 -S --subrepos recurse into subrepositories
871
871
872 (some details hidden, use --verbose to show complete help)
872 (some details hidden, use --verbose to show complete help)
873
873
874
874
875
875
876
876
877
877
878
878
879
879
880
880
881
881
882
882
883 $ hg help --extension extdiff
883 $ hg help --extension extdiff
884 extdiff extension - command to allow external programs to compare revisions
884 extdiff extension - command to allow external programs to compare revisions
885
885
886 The extdiff Mercurial extension allows you to use external programs to compare
886 The extdiff Mercurial extension allows you to use external programs to compare
887 revisions, or revision with working directory. The external diff programs are
887 revisions, or revision with working directory. The external diff programs are
888 called with a configurable set of options and two non-option arguments: paths
888 called with a configurable set of options and two non-option arguments: paths
889 to directories containing snapshots of files to compare.
889 to directories containing snapshots of files to compare.
890
890
891 If there is more than one file being compared and the "child" revision is the
891 If there is more than one file being compared and the "child" revision is the
892 working directory, any modifications made in the external diff program will be
892 working directory, any modifications made in the external diff program will be
893 copied back to the working directory from the temporary directory.
893 copied back to the working directory from the temporary directory.
894
894
895 The extdiff extension also allows you to configure new diff commands, so you
895 The extdiff extension also allows you to configure new diff commands, so you
896 do not need to type 'hg extdiff -p kdiff3' always.
896 do not need to type 'hg extdiff -p kdiff3' always.
897
897
898 [extdiff]
898 [extdiff]
899 # add new command that runs GNU diff(1) in 'context diff' mode
899 # add new command that runs GNU diff(1) in 'context diff' mode
900 cdiff = gdiff -Nprc5
900 cdiff = gdiff -Nprc5
901 ## or the old way:
901 ## or the old way:
902 #cmd.cdiff = gdiff
902 #cmd.cdiff = gdiff
903 #opts.cdiff = -Nprc5
903 #opts.cdiff = -Nprc5
904
904
905 # add new command called meld, runs meld (no need to name twice). If
905 # add new command called meld, runs meld (no need to name twice). If
906 # the meld executable is not available, the meld tool in [merge-tools]
906 # the meld executable is not available, the meld tool in [merge-tools]
907 # will be used, if available
907 # will be used, if available
908 meld =
908 meld =
909
909
910 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
910 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
911 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
911 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
912 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
912 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
913 # your .vimrc
913 # your .vimrc
914 vimdiff = gvim -f "+next" \
914 vimdiff = gvim -f "+next" \
915 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
915 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
916
916
917 Tool arguments can include variables that are expanded at runtime:
917 Tool arguments can include variables that are expanded at runtime:
918
918
919 $parent1, $plabel1 - filename, descriptive label of first parent
919 $parent1, $plabel1 - filename, descriptive label of first parent
920 $child, $clabel - filename, descriptive label of child revision
920 $child, $clabel - filename, descriptive label of child revision
921 $parent2, $plabel2 - filename, descriptive label of second parent
921 $parent2, $plabel2 - filename, descriptive label of second parent
922 $root - repository root
922 $root - repository root
923 $parent is an alias for $parent1.
923 $parent is an alias for $parent1.
924
924
925 The extdiff extension will look in your [diff-tools] and [merge-tools]
925 The extdiff extension will look in your [diff-tools] and [merge-tools]
926 sections for diff tool arguments, when none are specified in [extdiff].
926 sections for diff tool arguments, when none are specified in [extdiff].
927
927
928 [extdiff]
928 [extdiff]
929 kdiff3 =
929 kdiff3 =
930
930
931 [diff-tools]
931 [diff-tools]
932 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
932 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
933
933
934 If a program has a graphical interface, it might be interesting to tell
934 If a program has a graphical interface, it might be interesting to tell
935 Mercurial about it. It will prevent the program from being mistakenly used in
935 Mercurial about it. It will prevent the program from being mistakenly used in
936 a terminal-only environment (such as an SSH terminal session), and will make
936 a terminal-only environment (such as an SSH terminal session), and will make
937 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
937 'hg extdiff --per-file' open multiple file diffs at once instead of one by one
938 (if you still want to open file diffs one by one, you can use the --confirm
938 (if you still want to open file diffs one by one, you can use the --confirm
939 option).
939 option).
940
940
941 Declaring that a tool has a graphical interface can be done with the "gui"
941 Declaring that a tool has a graphical interface can be done with the "gui"
942 flag next to where "diffargs" are specified:
942 flag next to where "diffargs" are specified:
943
943
944 [diff-tools]
944 [diff-tools]
945 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
945 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
946 kdiff3.gui = true
946 kdiff3.gui = true
947
947
948 You can use -I/-X and list of file or directory names like normal 'hg diff'
948 You can use -I/-X and list of file or directory names like normal 'hg diff'
949 command. The extdiff extension makes snapshots of only needed files, so
949 command. The extdiff extension makes snapshots of only needed files, so
950 running the external diff program will actually be pretty fast (at least
950 running the external diff program will actually be pretty fast (at least
951 faster than having to compare the entire tree).
951 faster than having to compare the entire tree).
952
952
953 list of commands:
953 list of commands:
954
954
955 extdiff use external program to diff repository (or selected files)
955 extdiff use external program to diff repository (or selected files)
956
956
957 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
957 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
958
958
959
959
960
960
961
961
962
962
963
963
964
964
965
965
966
966
967
967
968
968
969
969
970
970
971
971
972
972
973
973
974 $ echo 'extdiff = !' >> $HGRCPATH
974 $ echo 'extdiff = !' >> $HGRCPATH
975
975
976 Test help topic with same name as extension
976 Test help topic with same name as extension
977
977
978 $ cat > multirevs.py <<EOF
978 $ cat > multirevs.py <<EOF
979 > from mercurial import commands, registrar
979 > from mercurial import commands, registrar
980 > cmdtable = {}
980 > cmdtable = {}
981 > command = registrar.command(cmdtable)
981 > command = registrar.command(cmdtable)
982 > """multirevs extension
982 > """multirevs extension
983 > Big multi-line module docstring."""
983 > Big multi-line module docstring."""
984 > @command(b'multirevs', [], b'ARG', norepo=True)
984 > @command(b'multirevs', [], b'ARG', norepo=True)
985 > def multirevs(ui, repo, arg, *args, **opts):
985 > def multirevs(ui, repo, arg, *args, **opts):
986 > """multirevs command"""
986 > """multirevs command"""
987 > EOF
987 > EOF
988 $ echo "multirevs = multirevs.py" >> $HGRCPATH
988 $ echo "multirevs = multirevs.py" >> $HGRCPATH
989
989
990 $ hg help multirevs | tail
990 $ hg help multirevs | tail
991 used):
991 used):
992
992
993 hg update :@
993 hg update :@
994
994
995 - Show diff between tags 1.3 and 1.5 (this works because the first and the
995 - Show diff between tags 1.3 and 1.5 (this works because the first and the
996 last revisions of the revset are used):
996 last revisions of the revset are used):
997
997
998 hg diff -r 1.3::1.5
998 hg diff -r 1.3::1.5
999
999
1000 use 'hg help -c multirevs' to see help for the multirevs command
1000 use 'hg help -c multirevs' to see help for the multirevs command
1001
1001
1002
1002
1003
1003
1004
1004
1005
1005
1006
1006
1007 $ hg help -c multirevs
1007 $ hg help -c multirevs
1008 hg multirevs ARG
1008 hg multirevs ARG
1009
1009
1010 multirevs command
1010 multirevs command
1011
1011
1012 (some details hidden, use --verbose to show complete help)
1012 (some details hidden, use --verbose to show complete help)
1013
1013
1014
1014
1015
1015
1016 $ hg multirevs
1016 $ hg multirevs
1017 hg multirevs: invalid arguments
1017 hg multirevs: invalid arguments
1018 hg multirevs ARG
1018 hg multirevs ARG
1019
1019
1020 multirevs command
1020 multirevs command
1021
1021
1022 (use 'hg multirevs -h' to show more help)
1022 (use 'hg multirevs -h' to show more help)
1023 [10]
1023 [10]
1024
1024
1025
1025
1026
1026
1027 $ echo "multirevs = !" >> $HGRCPATH
1027 $ echo "multirevs = !" >> $HGRCPATH
1028
1028
1029 Issue811: Problem loading extensions twice (by site and by user)
1029 Issue811: Problem loading extensions twice (by site and by user)
1030
1030
1031 $ cat <<EOF >> $HGRCPATH
1031 $ cat <<EOF >> $HGRCPATH
1032 > mq =
1032 > mq =
1033 > strip =
1033 > strip =
1034 > hgext.mq =
1034 > hgext.mq =
1035 > hgext/mq =
1035 > hgext/mq =
1036 > EOF
1036 > EOF
1037
1037
1038 Show extensions:
1038 Show extensions:
1039 (note that mq force load strip, also checking it's not loaded twice)
1039 (note that mq force load strip, also checking it's not loaded twice)
1040
1040
1041 #if no-extraextensions
1041 #if no-extraextensions
1042 $ hg debugextensions
1042 $ hg debugextensions
1043 mq
1043 mq
1044 strip
1044 strip
1045 #endif
1045 #endif
1046
1046
1047 For extensions, which name matches one of its commands, help
1047 For extensions, which name matches one of its commands, help
1048 message should ask '-v -e' to get list of built-in aliases
1048 message should ask '-v -e' to get list of built-in aliases
1049 along with extension help itself
1049 along with extension help itself
1050
1050
1051 $ mkdir $TESTTMP/d
1051 $ mkdir $TESTTMP/d
1052 $ cat > $TESTTMP/d/dodo.py <<EOF
1052 $ cat > $TESTTMP/d/dodo.py <<EOF
1053 > """
1053 > """
1054 > This is an awesome 'dodo' extension. It does nothing and
1054 > This is an awesome 'dodo' extension. It does nothing and
1055 > writes 'Foo foo'
1055 > writes 'Foo foo'
1056 > """
1056 > """
1057 > from mercurial import commands, registrar
1057 > from mercurial import commands, registrar
1058 > cmdtable = {}
1058 > cmdtable = {}
1059 > command = registrar.command(cmdtable)
1059 > command = registrar.command(cmdtable)
1060 > @command(b'dodo', [], b'hg dodo')
1060 > @command(b'dodo', [], b'hg dodo')
1061 > def dodo(ui, *args, **kwargs):
1061 > def dodo(ui, *args, **kwargs):
1062 > """Does nothing"""
1062 > """Does nothing"""
1063 > ui.write(b"I do nothing. Yay\\n")
1063 > ui.write(b"I do nothing. Yay\\n")
1064 > @command(b'foofoo', [], b'hg foofoo')
1064 > @command(b'foofoo', [], b'hg foofoo')
1065 > def foofoo(ui, *args, **kwargs):
1065 > def foofoo(ui, *args, **kwargs):
1066 > """Writes 'Foo foo'"""
1066 > """Writes 'Foo foo'"""
1067 > ui.write(b"Foo foo\\n")
1067 > ui.write(b"Foo foo\\n")
1068 > EOF
1068 > EOF
1069 $ dodopath=$TESTTMP/d/dodo.py
1069 $ dodopath=$TESTTMP/d/dodo.py
1070
1070
1071 $ echo "dodo = $dodopath" >> $HGRCPATH
1071 $ echo "dodo = $dodopath" >> $HGRCPATH
1072
1072
1073 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1073 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
1074 $ hg help -e dodo
1074 $ hg help -e dodo
1075 dodo extension -
1075 dodo extension -
1076
1076
1077 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1077 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1078
1078
1079 list of commands:
1079 list of commands:
1080
1080
1081 dodo Does nothing
1081 dodo Does nothing
1082 foofoo Writes 'Foo foo'
1082 foofoo Writes 'Foo foo'
1083
1083
1084 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1084 (use 'hg help -v -e dodo' to show built-in aliases and global options)
1085
1085
1086 Make sure that '-v -e' prints list of built-in aliases along with
1086 Make sure that '-v -e' prints list of built-in aliases along with
1087 extension help itself
1087 extension help itself
1088 $ hg help -v -e dodo
1088 $ hg help -v -e dodo
1089 dodo extension -
1089 dodo extension -
1090
1090
1091 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1091 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
1092
1092
1093 list of commands:
1093 list of commands:
1094
1094
1095 dodo Does nothing
1095 dodo Does nothing
1096 foofoo Writes 'Foo foo'
1096 foofoo Writes 'Foo foo'
1097
1097
1098 global options ([+] can be repeated):
1098 global options ([+] can be repeated):
1099
1099
1100 -R --repository REPO repository root directory or name of overlay bundle
1100 -R --repository REPO repository root directory or name of overlay bundle
1101 file
1101 file
1102 --cwd DIR change working directory
1102 --cwd DIR change working directory
1103 -y --noninteractive do not prompt, automatically pick the first choice for
1103 -y --noninteractive do not prompt, automatically pick the first choice for
1104 all prompts
1104 all prompts
1105 -q --quiet suppress output
1105 -q --quiet suppress output
1106 -v --verbose enable additional output
1106 -v --verbose enable additional output
1107 --color TYPE when to colorize (boolean, always, auto, never, or
1107 --color TYPE when to colorize (boolean, always, auto, never, or
1108 debug)
1108 debug)
1109 --config CONFIG [+] set/override config option (use 'section.name=value')
1109 --config CONFIG [+] set/override config option (use 'section.name=value')
1110 --debug enable debugging output
1110 --debug enable debugging output
1111 --debugger start debugger
1111 --debugger start debugger
1112 --encoding ENCODE set the charset encoding (default: ascii)
1112 --encoding ENCODE set the charset encoding (default: ascii)
1113 --encodingmode MODE set the charset encoding mode (default: strict)
1113 --encodingmode MODE set the charset encoding mode (default: strict)
1114 --traceback always print a traceback on exception
1114 --traceback always print a traceback on exception
1115 --time time how long the command takes
1115 --time time how long the command takes
1116 --profile print command execution profile
1116 --profile print command execution profile
1117 --version output version information and exit
1117 --version output version information and exit
1118 -h --help display help and exit
1118 -h --help display help and exit
1119 --hidden consider hidden changesets
1119 --hidden consider hidden changesets
1120 --pager TYPE when to paginate (boolean, always, auto, or never)
1120 --pager TYPE when to paginate (boolean, always, auto, or never)
1121 (default: auto)
1121 (default: auto)
1122
1122
1123 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1123 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
1124 $ hg help -v dodo
1124 $ hg help -v dodo
1125 hg dodo
1125 hg dodo
1126
1126
1127 Does nothing
1127 Does nothing
1128
1128
1129 (use 'hg help -e dodo' to show help for the dodo extension)
1129 (use 'hg help -e dodo' to show help for the dodo extension)
1130
1130
1131 options:
1131 options:
1132
1132
1133 --mq operate on patch repository
1133 --mq operate on patch repository
1134
1134
1135 global options ([+] can be repeated):
1135 global options ([+] can be repeated):
1136
1136
1137 -R --repository REPO repository root directory or name of overlay bundle
1137 -R --repository REPO repository root directory or name of overlay bundle
1138 file
1138 file
1139 --cwd DIR change working directory
1139 --cwd DIR change working directory
1140 -y --noninteractive do not prompt, automatically pick the first choice for
1140 -y --noninteractive do not prompt, automatically pick the first choice for
1141 all prompts
1141 all prompts
1142 -q --quiet suppress output
1142 -q --quiet suppress output
1143 -v --verbose enable additional output
1143 -v --verbose enable additional output
1144 --color TYPE when to colorize (boolean, always, auto, never, or
1144 --color TYPE when to colorize (boolean, always, auto, never, or
1145 debug)
1145 debug)
1146 --config CONFIG [+] set/override config option (use 'section.name=value')
1146 --config CONFIG [+] set/override config option (use 'section.name=value')
1147 --debug enable debugging output
1147 --debug enable debugging output
1148 --debugger start debugger
1148 --debugger start debugger
1149 --encoding ENCODE set the charset encoding (default: ascii)
1149 --encoding ENCODE set the charset encoding (default: ascii)
1150 --encodingmode MODE set the charset encoding mode (default: strict)
1150 --encodingmode MODE set the charset encoding mode (default: strict)
1151 --traceback always print a traceback on exception
1151 --traceback always print a traceback on exception
1152 --time time how long the command takes
1152 --time time how long the command takes
1153 --profile print command execution profile
1153 --profile print command execution profile
1154 --version output version information and exit
1154 --version output version information and exit
1155 -h --help display help and exit
1155 -h --help display help and exit
1156 --hidden consider hidden changesets
1156 --hidden consider hidden changesets
1157 --pager TYPE when to paginate (boolean, always, auto, or never)
1157 --pager TYPE when to paginate (boolean, always, auto, or never)
1158 (default: auto)
1158 (default: auto)
1159
1159
1160 In case when extension name doesn't match any of its commands,
1160 In case when extension name doesn't match any of its commands,
1161 help message should ask for '-v' to get list of built-in aliases
1161 help message should ask for '-v' to get list of built-in aliases
1162 along with extension help
1162 along with extension help
1163 $ cat > $TESTTMP/d/dudu.py <<EOF
1163 $ cat > $TESTTMP/d/dudu.py <<EOF
1164 > """
1164 > """
1165 > This is an awesome 'dudu' extension. It does something and
1165 > This is an awesome 'dudu' extension. It does something and
1166 > also writes 'Beep beep'
1166 > also writes 'Beep beep'
1167 > """
1167 > """
1168 > from mercurial import commands, registrar
1168 > from mercurial import commands, registrar
1169 > cmdtable = {}
1169 > cmdtable = {}
1170 > command = registrar.command(cmdtable)
1170 > command = registrar.command(cmdtable)
1171 > @command(b'something', [], b'hg something')
1171 > @command(b'something', [], b'hg something')
1172 > def something(ui, *args, **kwargs):
1172 > def something(ui, *args, **kwargs):
1173 > """Does something"""
1173 > """Does something"""
1174 > ui.write(b"I do something. Yaaay\\n")
1174 > ui.write(b"I do something. Yaaay\\n")
1175 > @command(b'beep', [], b'hg beep')
1175 > @command(b'beep', [], b'hg beep')
1176 > def beep(ui, *args, **kwargs):
1176 > def beep(ui, *args, **kwargs):
1177 > """Writes 'Beep beep'"""
1177 > """Writes 'Beep beep'"""
1178 > ui.write(b"Beep beep\\n")
1178 > ui.write(b"Beep beep\\n")
1179 > EOF
1179 > EOF
1180 $ dudupath=$TESTTMP/d/dudu.py
1180 $ dudupath=$TESTTMP/d/dudu.py
1181
1181
1182 $ echo "dudu = $dudupath" >> $HGRCPATH
1182 $ echo "dudu = $dudupath" >> $HGRCPATH
1183
1183
1184 $ hg help -e dudu
1184 $ hg help -e dudu
1185 dudu extension -
1185 dudu extension -
1186
1186
1187 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1187 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1188 beep'
1188 beep'
1189
1189
1190 list of commands:
1190 list of commands:
1191
1191
1192 beep Writes 'Beep beep'
1192 beep Writes 'Beep beep'
1193 something Does something
1193 something Does something
1194
1194
1195 (use 'hg help -v dudu' to show built-in aliases and global options)
1195 (use 'hg help -v dudu' to show built-in aliases and global options)
1196
1196
1197 In case when extension name doesn't match any of its commands,
1197 In case when extension name doesn't match any of its commands,
1198 help options '-v' and '-v -e' should be equivalent
1198 help options '-v' and '-v -e' should be equivalent
1199 $ hg help -v dudu
1199 $ hg help -v dudu
1200 dudu extension -
1200 dudu extension -
1201
1201
1202 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1202 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1203 beep'
1203 beep'
1204
1204
1205 list of commands:
1205 list of commands:
1206
1206
1207 beep Writes 'Beep beep'
1207 beep Writes 'Beep beep'
1208 something Does something
1208 something Does something
1209
1209
1210 global options ([+] can be repeated):
1210 global options ([+] can be repeated):
1211
1211
1212 -R --repository REPO repository root directory or name of overlay bundle
1212 -R --repository REPO repository root directory or name of overlay bundle
1213 file
1213 file
1214 --cwd DIR change working directory
1214 --cwd DIR change working directory
1215 -y --noninteractive do not prompt, automatically pick the first choice for
1215 -y --noninteractive do not prompt, automatically pick the first choice for
1216 all prompts
1216 all prompts
1217 -q --quiet suppress output
1217 -q --quiet suppress output
1218 -v --verbose enable additional output
1218 -v --verbose enable additional output
1219 --color TYPE when to colorize (boolean, always, auto, never, or
1219 --color TYPE when to colorize (boolean, always, auto, never, or
1220 debug)
1220 debug)
1221 --config CONFIG [+] set/override config option (use 'section.name=value')
1221 --config CONFIG [+] set/override config option (use 'section.name=value')
1222 --debug enable debugging output
1222 --debug enable debugging output
1223 --debugger start debugger
1223 --debugger start debugger
1224 --encoding ENCODE set the charset encoding (default: ascii)
1224 --encoding ENCODE set the charset encoding (default: ascii)
1225 --encodingmode MODE set the charset encoding mode (default: strict)
1225 --encodingmode MODE set the charset encoding mode (default: strict)
1226 --traceback always print a traceback on exception
1226 --traceback always print a traceback on exception
1227 --time time how long the command takes
1227 --time time how long the command takes
1228 --profile print command execution profile
1228 --profile print command execution profile
1229 --version output version information and exit
1229 --version output version information and exit
1230 -h --help display help and exit
1230 -h --help display help and exit
1231 --hidden consider hidden changesets
1231 --hidden consider hidden changesets
1232 --pager TYPE when to paginate (boolean, always, auto, or never)
1232 --pager TYPE when to paginate (boolean, always, auto, or never)
1233 (default: auto)
1233 (default: auto)
1234
1234
1235 $ hg help -v -e dudu
1235 $ hg help -v -e dudu
1236 dudu extension -
1236 dudu extension -
1237
1237
1238 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1238 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1239 beep'
1239 beep'
1240
1240
1241 list of commands:
1241 list of commands:
1242
1242
1243 beep Writes 'Beep beep'
1243 beep Writes 'Beep beep'
1244 something Does something
1244 something Does something
1245
1245
1246 global options ([+] can be repeated):
1246 global options ([+] can be repeated):
1247
1247
1248 -R --repository REPO repository root directory or name of overlay bundle
1248 -R --repository REPO repository root directory or name of overlay bundle
1249 file
1249 file
1250 --cwd DIR change working directory
1250 --cwd DIR change working directory
1251 -y --noninteractive do not prompt, automatically pick the first choice for
1251 -y --noninteractive do not prompt, automatically pick the first choice for
1252 all prompts
1252 all prompts
1253 -q --quiet suppress output
1253 -q --quiet suppress output
1254 -v --verbose enable additional output
1254 -v --verbose enable additional output
1255 --color TYPE when to colorize (boolean, always, auto, never, or
1255 --color TYPE when to colorize (boolean, always, auto, never, or
1256 debug)
1256 debug)
1257 --config CONFIG [+] set/override config option (use 'section.name=value')
1257 --config CONFIG [+] set/override config option (use 'section.name=value')
1258 --debug enable debugging output
1258 --debug enable debugging output
1259 --debugger start debugger
1259 --debugger start debugger
1260 --encoding ENCODE set the charset encoding (default: ascii)
1260 --encoding ENCODE set the charset encoding (default: ascii)
1261 --encodingmode MODE set the charset encoding mode (default: strict)
1261 --encodingmode MODE set the charset encoding mode (default: strict)
1262 --traceback always print a traceback on exception
1262 --traceback always print a traceback on exception
1263 --time time how long the command takes
1263 --time time how long the command takes
1264 --profile print command execution profile
1264 --profile print command execution profile
1265 --version output version information and exit
1265 --version output version information and exit
1266 -h --help display help and exit
1266 -h --help display help and exit
1267 --hidden consider hidden changesets
1267 --hidden consider hidden changesets
1268 --pager TYPE when to paginate (boolean, always, auto, or never)
1268 --pager TYPE when to paginate (boolean, always, auto, or never)
1269 (default: auto)
1269 (default: auto)
1270
1270
1271 Disabled extension commands:
1271 Disabled extension commands:
1272
1272
1273 $ ORGHGRCPATH=$HGRCPATH
1273 $ ORGHGRCPATH=$HGRCPATH
1274 $ HGRCPATH=
1274 $ HGRCPATH=
1275 $ export HGRCPATH
1275 $ export HGRCPATH
1276 $ hg help email
1276 $ hg help email
1277 'email' is provided by the following extension:
1277 'email' is provided by the following extension:
1278
1278
1279 patchbomb command to send changesets as (a series of) patch emails
1279 patchbomb command to send changesets as (a series of) patch emails
1280
1280
1281 (use 'hg help extensions' for information on enabling extensions)
1281 (use 'hg help extensions' for information on enabling extensions)
1282
1282
1283
1283
1284 $ hg qdel
1284 $ hg qdel
1285 hg: unknown command 'qdel'
1285 hg: unknown command 'qdel'
1286 'qdelete' is provided by the following extension:
1286 'qdelete' is provided by the following extension:
1287
1287
1288 mq manage a stack of patches
1288 mq manage a stack of patches
1289
1289
1290 (use 'hg help extensions' for information on enabling extensions)
1290 (use 'hg help extensions' for information on enabling extensions)
1291 [255]
1291 [255]
1292
1292
1293
1293
1294 $ hg churn
1294 $ hg churn
1295 hg: unknown command 'churn'
1295 hg: unknown command 'churn'
1296 'churn' is provided by the following extension:
1296 'churn' is provided by the following extension:
1297
1297
1298 churn command to display statistics about repository history
1298 churn command to display statistics about repository history
1299
1299
1300 (use 'hg help extensions' for information on enabling extensions)
1300 (use 'hg help extensions' for information on enabling extensions)
1301 [255]
1301 [255]
1302
1302
1303
1303
1304
1304
1305 Disabled extensions:
1305 Disabled extensions:
1306
1306
1307 $ hg help churn
1307 $ hg help churn
1308 churn extension - command to display statistics about repository history
1308 churn extension - command to display statistics about repository history
1309
1309
1310 (use 'hg help extensions' for information on enabling extensions)
1310 (use 'hg help extensions' for information on enabling extensions)
1311
1311
1312 $ hg help patchbomb
1312 $ hg help patchbomb
1313 patchbomb extension - command to send changesets as (a series of) patch emails
1313 patchbomb extension - command to send changesets as (a series of) patch emails
1314
1314
1315 The series is started off with a "[PATCH 0 of N]" introduction, which
1315 The series is started off with a "[PATCH 0 of N]" introduction, which
1316 describes the series as a whole.
1316 describes the series as a whole.
1317
1317
1318 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1318 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1319 line of the changeset description as the subject text. The message contains
1319 line of the changeset description as the subject text. The message contains
1320 two or three body parts:
1320 two or three body parts:
1321
1321
1322 - The changeset description.
1322 - The changeset description.
1323 - [Optional] The result of running diffstat on the patch.
1323 - [Optional] The result of running diffstat on the patch.
1324 - The patch itself, as generated by 'hg export'.
1324 - The patch itself, as generated by 'hg export'.
1325
1325
1326 Each message refers to the first in the series using the In-Reply-To and
1326 Each message refers to the first in the series using the In-Reply-To and
1327 References headers, so they will show up as a sequence in threaded mail and
1327 References headers, so they will show up as a sequence in threaded mail and
1328 news readers, and in mail archives.
1328 news readers, and in mail archives.
1329
1329
1330 To configure other defaults, add a section like this to your configuration
1330 To configure other defaults, add a section like this to your configuration
1331 file:
1331 file:
1332
1332
1333 [email]
1333 [email]
1334 from = My Name <my@email>
1334 from = My Name <my@email>
1335 to = recipient1, recipient2, ...
1335 to = recipient1, recipient2, ...
1336 cc = cc1, cc2, ...
1336 cc = cc1, cc2, ...
1337 bcc = bcc1, bcc2, ...
1337 bcc = bcc1, bcc2, ...
1338 reply-to = address1, address2, ...
1338 reply-to = address1, address2, ...
1339
1339
1340 Use "[patchbomb]" as configuration section name if you need to override global
1340 Use "[patchbomb]" as configuration section name if you need to override global
1341 "[email]" address settings.
1341 "[email]" address settings.
1342
1342
1343 Then you can use the 'hg email' command to mail a series of changesets as a
1343 Then you can use the 'hg email' command to mail a series of changesets as a
1344 patchbomb.
1344 patchbomb.
1345
1345
1346 You can also either configure the method option in the email section to be a
1346 You can also either configure the method option in the email section to be a
1347 sendmail compatible mailer or fill out the [smtp] section so that the
1347 sendmail compatible mailer or fill out the [smtp] section so that the
1348 patchbomb extension can automatically send patchbombs directly from the
1348 patchbomb extension can automatically send patchbombs directly from the
1349 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1349 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1350
1350
1351 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1351 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1352 supply one via configuration or the command line. You can override this to
1352 supply one via configuration or the command line. You can override this to
1353 never prompt by configuring an empty value:
1353 never prompt by configuring an empty value:
1354
1354
1355 [email]
1355 [email]
1356 cc =
1356 cc =
1357
1357
1358 You can control the default inclusion of an introduction message with the
1358 You can control the default inclusion of an introduction message with the
1359 "patchbomb.intro" configuration option. The configuration is always
1359 "patchbomb.intro" configuration option. The configuration is always
1360 overwritten by command line flags like --intro and --desc:
1360 overwritten by command line flags like --intro and --desc:
1361
1361
1362 [patchbomb]
1362 [patchbomb]
1363 intro=auto # include introduction message if more than 1 patch (default)
1363 intro=auto # include introduction message if more than 1 patch (default)
1364 intro=never # never include an introduction message
1364 intro=never # never include an introduction message
1365 intro=always # always include an introduction message
1365 intro=always # always include an introduction message
1366
1366
1367 You can specify a template for flags to be added in subject prefixes. Flags
1367 You can specify a template for flags to be added in subject prefixes. Flags
1368 specified by --flag option are exported as "{flags}" keyword:
1368 specified by --flag option are exported as "{flags}" keyword:
1369
1369
1370 [patchbomb]
1370 [patchbomb]
1371 flagtemplate = "{separate(' ',
1371 flagtemplate = "{separate(' ',
1372 ifeq(branch, 'default', '', branch|upper),
1372 ifeq(branch, 'default', '', branch|upper),
1373 flags)}"
1373 flags)}"
1374
1374
1375 You can set patchbomb to always ask for confirmation by setting
1375 You can set patchbomb to always ask for confirmation by setting
1376 "patchbomb.confirm" to true.
1376 "patchbomb.confirm" to true.
1377
1377
1378 (use 'hg help extensions' for information on enabling extensions)
1378 (use 'hg help extensions' for information on enabling extensions)
1379
1379
1380
1380
1381 Help can find unimported extensions
1381 Help can find unimported extensions
1382 -----------------------------------
1382 -----------------------------------
1383
1383
1384 XXX-PYOXIDIZER since the frozen binary does not have source directory tree,
1384 XXX-PYOXIDIZER since the frozen binary does not have source directory tree,
1385 this make the checking for actual file under `hgext` a bit complicated. In
1385 this make the checking for actual file under `hgext` a bit complicated. In
1386 addition these tests do some strange dance to ensure some other module are the
1386 addition these tests do some strange dance to ensure some other module are the
1387 first in `sys.path` (since the current install path is always in front
1387 first in `sys.path` (since the current install path is always in front
1388 otherwise) that are fragile and that does not match reality in the field. So
1388 otherwise) that are fragile and that does not match reality in the field. So
1389 for now we disable this test untill a deeper rework of that logic is done.
1389 for now we disable this test untill a deeper rework of that logic is done.
1390
1390
1391 #if no-pyoxidizer
1391 #if no-pyoxidizer
1392
1392
1393 Broken disabled extension and command:
1393 Broken disabled extension and command:
1394
1394
1395 $ mkdir hgext
1395 $ mkdir hgext
1396 $ echo > hgext/__init__.py
1396 $ echo > hgext/__init__.py
1397 $ cat > hgext/broken.py <<NO_CHECK_EOF
1397 $ cat > hgext/broken.py <<NO_CHECK_EOF
1398 > "broken extension'
1398 > "broken extension'
1399 > NO_CHECK_EOF
1399 > NO_CHECK_EOF
1400 $ cat > path.py <<EOF
1400 $ cat > path.py <<EOF
1401 > import os
1401 > import os
1402 > import sys
1402 > import sys
1403 > sys.path.insert(0, os.environ['HGEXTPATH'])
1403 > sys.path.insert(0, os.environ['HGEXTPATH'])
1404 > EOF
1404 > EOF
1405 $ HGEXTPATH=`pwd`
1405 $ HGEXTPATH=`pwd`
1406 $ export HGEXTPATH
1406 $ export HGEXTPATH
1407
1407
1408 $ hg --config extensions.path=./path.py help broken
1408 $ hg --config extensions.path=./path.py help broken
1409 broken extension - (no help text available)
1409 broken extension - (no help text available)
1410
1410
1411 (use 'hg help extensions' for information on enabling extensions)
1411 (use 'hg help extensions' for information on enabling extensions)
1412
1412
1413
1413
1414 $ cat > hgext/forest.py <<EOF
1414 $ cat > hgext/forest.py <<EOF
1415 > cmdtable = None
1415 > cmdtable = None
1416 > @command()
1416 > @command()
1417 > def f():
1417 > def f():
1418 > pass
1418 > pass
1419 > @command(123)
1419 > @command(123)
1420 > def g():
1420 > def g():
1421 > pass
1421 > pass
1422 > EOF
1422 > EOF
1423 $ hg --config extensions.path=./path.py help foo
1423 $ hg --config extensions.path=./path.py help foo
1424 abort: no such help topic: foo
1424 abort: no such help topic: foo
1425 (try 'hg help --keyword foo')
1425 (try 'hg help --keyword foo')
1426 [255]
1426 [255]
1427
1427
1428 #endif
1428 #endif
1429
1429
1430 ---
1430 ---
1431
1431
1432 $ cat > throw.py <<EOF
1432 $ cat > throw.py <<EOF
1433 > from mercurial import commands, registrar, util
1433 > from mercurial import commands, registrar, util
1434 > cmdtable = {}
1434 > cmdtable = {}
1435 > command = registrar.command(cmdtable)
1435 > command = registrar.command(cmdtable)
1436 > class Bogon(Exception): pass
1436 > class Bogon(Exception): pass
1437 > # NB: version should be bytes; simulating extension not ported to py3
1437 > # NB: version should be bytes; simulating extension not ported to py3
1438 > __version__ = '1.0.0'
1438 > __version__ = '1.0.0'
1439 > @command(b'throw', [], b'hg throw', norepo=True)
1439 > @command(b'throw', [], b'hg throw', norepo=True)
1440 > def throw(ui, **opts):
1440 > def throw(ui, **opts):
1441 > """throws an exception"""
1441 > """throws an exception"""
1442 > raise Bogon()
1442 > raise Bogon()
1443 > EOF
1443 > EOF
1444
1444
1445 Test extension without proper byteification of key attributes doesn't crash when
1445 Test extension without proper byteification of key attributes doesn't crash when
1446 accessed.
1446 accessed.
1447
1447
1448 $ hg version -v --config extensions.throw=throw.py | grep '^ '
1448 $ hg version -v --config extensions.throw=throw.py | grep '^ '
1449 throw external 1.0.0
1449 throw external 1.0.0
1450
1450
1451 No declared supported version, extension complains:
1451 No declared supported version, extension complains:
1452 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1452 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1453 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1453 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1454 ** which supports versions unknown of Mercurial.
1454 ** which supports versions unknown of Mercurial.
1455 ** Please disable "throw" and try your action again.
1455 ** Please disable "throw" and try your action again.
1456 ** If that fixes the bug please report it to the extension author.
1456 ** If that fixes the bug please report it to the extension author.
1457 ** Python * (glob)
1457 ** Python * (glob)
1458 ** Mercurial Distributed SCM * (glob)
1458 ** Mercurial Distributed SCM * (glob)
1459 ** Extensions loaded: throw 1.0.0
1459 ** Extensions loaded: throw 1.0.0
1460
1460
1461 empty declaration of supported version, extension complains (but doesn't choke if
1461 empty declaration of supported version, extension complains (but doesn't choke if
1462 the value is improperly a str instead of bytes):
1462 the value is improperly a str instead of bytes):
1463 $ echo "testedwith = ''" >> throw.py
1463 $ echo "testedwith = ''" >> throw.py
1464 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1464 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1465 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1465 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1466 ** which supports versions unknown of Mercurial.
1466 ** which supports versions unknown of Mercurial.
1467 ** Please disable "throw" and try your action again.
1467 ** Please disable "throw" and try your action again.
1468 ** If that fixes the bug please report it to the extension author.
1468 ** If that fixes the bug please report it to the extension author.
1469 ** Python * (glob)
1469 ** Python * (glob)
1470 ** Mercurial Distributed SCM (*) (glob)
1470 ** Mercurial Distributed SCM (*) (glob)
1471 ** Extensions loaded: throw 1.0.0
1471 ** Extensions loaded: throw 1.0.0
1472
1472
1473 If the extension specifies a buglink, show that (but don't choke if the value is
1473 If the extension specifies a buglink, show that (but don't choke if the value is
1474 improperly a str instead of bytes):
1474 improperly a str instead of bytes):
1475 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1475 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1476 $ rm -f throw.pyc throw.pyo
1476 $ rm -f throw.pyc throw.pyo
1477 $ rm -Rf __pycache__
1477 $ rm -Rf __pycache__
1478 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1478 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1479 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1479 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1480 ** which supports versions unknown of Mercurial.
1480 ** which supports versions unknown of Mercurial.
1481 ** Please disable "throw" and try your action again.
1481 ** Please disable "throw" and try your action again.
1482 ** If that fixes the bug please report it to http://example.com/bts
1482 ** If that fixes the bug please report it to http://example.com/bts
1483 ** Python * (glob)
1483 ** Python * (glob)
1484 ** Mercurial Distributed SCM (*) (glob)
1484 ** Mercurial Distributed SCM (*) (glob)
1485 ** Extensions loaded: throw 1.0.0
1485 ** Extensions loaded: throw 1.0.0
1486
1486
1487 If the extensions declare outdated versions, accuse the older extension first:
1487 If the extensions declare outdated versions, accuse the older extension first:
1488 $ echo "from mercurial import util" >> older.py
1488 $ echo "from mercurial import util" >> older.py
1489 $ echo "util.version = lambda:b'2.2'" >> older.py
1489 $ echo "util.version = lambda:b'2.2'" >> older.py
1490 $ echo "testedwith = b'1.9.3'" >> older.py
1490 $ echo "testedwith = b'1.9.3'" >> older.py
1491 $ echo "testedwith = b'2.1.1'" >> throw.py
1491 $ echo "testedwith = b'2.1.1'" >> throw.py
1492 $ rm -f throw.pyc throw.pyo
1492 $ rm -f throw.pyc throw.pyo
1493 $ rm -Rf __pycache__
1493 $ rm -Rf __pycache__
1494 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1494 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1495 > throw 2>&1 | egrep '^\*\*'
1495 > throw 2>&1 | egrep '^\*\*'
1496 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1496 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1497 ** which supports versions 1.9 of Mercurial.
1497 ** which supports versions 1.9 of Mercurial.
1498 ** Please disable "older" and try your action again.
1498 ** Please disable "older" and try your action again.
1499 ** If that fixes the bug please report it to the extension author.
1499 ** If that fixes the bug please report it to the extension author.
1500 ** Python * (glob)
1500 ** Python * (glob)
1501 ** Mercurial Distributed SCM (version 2.2)
1501 ** Mercurial Distributed SCM (version 2.2)
1502 ** Extensions loaded: older, throw 1.0.0
1502 ** Extensions loaded: older, throw 1.0.0
1503
1503
1504 One extension only tested with older, one only with newer versions:
1504 One extension only tested with older, one only with newer versions:
1505 $ echo "util.version = lambda:b'2.1'" >> older.py
1505 $ echo "util.version = lambda:b'2.1'" >> older.py
1506 $ rm -f older.pyc older.pyo
1506 $ rm -f older.pyc older.pyo
1507 $ rm -Rf __pycache__
1507 $ rm -Rf __pycache__
1508 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1508 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1509 > throw 2>&1 | egrep '^\*\*'
1509 > throw 2>&1 | egrep '^\*\*'
1510 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1510 ** Unknown exception encountered with possibly-broken third-party extension "older" (version N/A)
1511 ** which supports versions 1.9 of Mercurial.
1511 ** which supports versions 1.9 of Mercurial.
1512 ** Please disable "older" and try your action again.
1512 ** Please disable "older" and try your action again.
1513 ** If that fixes the bug please report it to the extension author.
1513 ** If that fixes the bug please report it to the extension author.
1514 ** Python * (glob)
1514 ** Python * (glob)
1515 ** Mercurial Distributed SCM (version 2.1)
1515 ** Mercurial Distributed SCM (version 2.1)
1516 ** Extensions loaded: older, throw 1.0.0
1516 ** Extensions loaded: older, throw 1.0.0
1517
1517
1518 Older extension is tested with current version, the other only with newer:
1518 Older extension is tested with current version, the other only with newer:
1519 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1519 $ echo "util.version = lambda:b'1.9.3'" >> older.py
1520 $ rm -f older.pyc older.pyo
1520 $ rm -f older.pyc older.pyo
1521 $ rm -Rf __pycache__
1521 $ rm -Rf __pycache__
1522 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1522 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1523 > throw 2>&1 | egrep '^\*\*'
1523 > throw 2>&1 | egrep '^\*\*'
1524 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1524 ** Unknown exception encountered with possibly-broken third-party extension "throw" 1.0.0
1525 ** which supports versions 2.1 of Mercurial.
1525 ** which supports versions 2.1 of Mercurial.
1526 ** Please disable "throw" and try your action again.
1526 ** Please disable "throw" and try your action again.
1527 ** If that fixes the bug please report it to http://example.com/bts
1527 ** If that fixes the bug please report it to http://example.com/bts
1528 ** Python * (glob)
1528 ** Python * (glob)
1529 ** Mercurial Distributed SCM (version 1.9.3)
1529 ** Mercurial Distributed SCM (version 1.9.3)
1530 ** Extensions loaded: older, throw 1.0.0
1530 ** Extensions loaded: older, throw 1.0.0
1531
1531
1532 Ability to point to a different point
1532 Ability to point to a different point
1533 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1533 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1534 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1534 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1535 ** unknown exception encountered, please report by visiting
1535 ** unknown exception encountered, please report by visiting
1536 ** Your Local Goat Lenders
1536 ** Your Local Goat Lenders
1537 ** Python * (glob)
1537 ** Python * (glob)
1538 ** Mercurial Distributed SCM (*) (glob)
1538 ** Mercurial Distributed SCM (*) (glob)
1539 ** Extensions loaded: older, throw 1.0.0
1539 ** Extensions loaded: older, throw 1.0.0
1540
1540
1541 Declare the version as supporting this hg version, show regular bts link:
1541 Declare the version as supporting this hg version, show regular bts link:
1542 $ hgver=`hg debuginstall -T '{hgver}'`
1542 $ hgver=`hg debuginstall -T '{hgver}'`
1543 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1543 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1544 $ if [ -z "$hgver" ]; then
1544 $ if [ -z "$hgver" ]; then
1545 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1545 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1546 > fi
1546 > fi
1547 $ rm -f throw.pyc throw.pyo
1547 $ rm -f throw.pyc throw.pyo
1548 $ rm -Rf __pycache__
1548 $ rm -Rf __pycache__
1549 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1549 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1550 ** unknown exception encountered, please report by visiting
1550 ** unknown exception encountered, please report by visiting
1551 ** https://mercurial-scm.org/wiki/BugTracker
1551 ** https://mercurial-scm.org/wiki/BugTracker
1552 ** Python * (glob)
1552 ** Python * (glob)
1553 ** Mercurial Distributed SCM (*) (glob)
1553 ** Mercurial Distributed SCM (*) (glob)
1554 ** Extensions loaded: throw 1.0.0
1554 ** Extensions loaded: throw 1.0.0
1555
1555
1556 Patch version is ignored during compatibility check
1556 Patch version is ignored during compatibility check
1557 $ echo "testedwith = b'3.2'" >> throw.py
1557 $ echo "testedwith = b'3.2'" >> throw.py
1558 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1558 $ echo "util.version = lambda:b'3.2.2'" >> throw.py
1559 $ rm -f throw.pyc throw.pyo
1559 $ rm -f throw.pyc throw.pyo
1560 $ rm -Rf __pycache__
1560 $ rm -Rf __pycache__
1561 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1561 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1562 ** unknown exception encountered, please report by visiting
1562 ** unknown exception encountered, please report by visiting
1563 ** https://mercurial-scm.org/wiki/BugTracker
1563 ** https://mercurial-scm.org/wiki/BugTracker
1564 ** Python * (glob)
1564 ** Python * (glob)
1565 ** Mercurial Distributed SCM (*) (glob)
1565 ** Mercurial Distributed SCM (*) (glob)
1566 ** Extensions loaded: throw 1.0.0
1566 ** Extensions loaded: throw 1.0.0
1567
1567
1568 Test version number support in 'hg version':
1568 Test version number support in 'hg version':
1569 $ echo '__version__ = (1, 2, 3)' >> throw.py
1569 $ echo '__version__ = (1, 2, 3)' >> throw.py
1570 $ rm -f throw.pyc throw.pyo
1570 $ rm -f throw.pyc throw.pyo
1571 $ rm -Rf __pycache__
1571 $ rm -Rf __pycache__
1572 $ hg version -v
1572 $ hg version -v
1573 Mercurial Distributed SCM (version *) (glob)
1573 Mercurial Distributed SCM (version *) (glob)
1574 (see https://mercurial-scm.org for more information)
1574 (see https://mercurial-scm.org for more information)
1575
1575
1576 Copyright (C) 2005-* Olivia Mackall and others (glob)
1576 Copyright (C) 2005-* Olivia Mackall and others (glob)
1577 This is free software; see the source for copying conditions. There is NO
1577 This is free software; see the source for copying conditions. There is NO
1578 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1578 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1579
1579
1580 Enabled extensions:
1580 Enabled extensions:
1581
1581
1582
1582
1583 $ hg version -v --config extensions.throw=throw.py
1583 $ hg version -v --config extensions.throw=throw.py
1584 Mercurial Distributed SCM (version *) (glob)
1584 Mercurial Distributed SCM (version *) (glob)
1585 (see https://mercurial-scm.org for more information)
1585 (see https://mercurial-scm.org for more information)
1586
1586
1587 Copyright (C) 2005-* Olivia Mackall and others (glob)
1587 Copyright (C) 2005-* Olivia Mackall and others (glob)
1588 This is free software; see the source for copying conditions. There is NO
1588 This is free software; see the source for copying conditions. There is NO
1589 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1589 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1590
1590
1591 Enabled extensions:
1591 Enabled extensions:
1592
1592
1593 throw external 1.2.3
1593 throw external 1.2.3
1594 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1594 $ echo 'getversion = lambda: b"1.twentythree"' >> throw.py
1595 $ rm -f throw.pyc throw.pyo
1595 $ rm -f throw.pyc throw.pyo
1596 $ rm -Rf __pycache__
1596 $ rm -Rf __pycache__
1597 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1597 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1598 Mercurial Distributed SCM (version *) (glob)
1598 Mercurial Distributed SCM (version *) (glob)
1599 (see https://mercurial-scm.org for more information)
1599 (see https://mercurial-scm.org for more information)
1600
1600
1601 Copyright (C) 2005-* Olivia Mackall and others (glob)
1601 Copyright (C) 2005-* Olivia Mackall and others (glob)
1602 This is free software; see the source for copying conditions. There is NO
1602 This is free software; see the source for copying conditions. There is NO
1603 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1603 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1604
1604
1605 Enabled extensions:
1605 Enabled extensions:
1606
1606
1607 strip internal
1607 strip internal
1608 throw external 1.twentythree
1608 throw external 1.twentythree
1609
1609
1610 $ hg version -q --config extensions.throw=throw.py
1610 $ hg version -q --config extensions.throw=throw.py
1611 Mercurial Distributed SCM (version *) (glob)
1611 Mercurial Distributed SCM (version *) (glob)
1612
1612
1613 Test template output:
1613 Test template output:
1614
1614
1615 $ hg version --config extensions.strip= -T'{extensions}'
1615 $ hg version --config extensions.strip= -T'{extensions}'
1616 strip
1616 strip
1617
1617
1618 Test JSON output of version:
1618 Test JSON output of version:
1619
1619
1620 $ hg version -Tjson
1620 $ hg version -Tjson
1621 [
1621 [
1622 {
1622 {
1623 "extensions": [],
1623 "extensions": [],
1624 "ver": "*" (glob)
1624 "ver": "*" (glob)
1625 }
1625 }
1626 ]
1626 ]
1627
1627
1628 $ hg version --config extensions.throw=throw.py -Tjson
1628 $ hg version --config extensions.throw=throw.py -Tjson
1629 [
1629 [
1630 {
1630 {
1631 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1631 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1632 "ver": "3.2.2"
1632 "ver": "3.2.2"
1633 }
1633 }
1634 ]
1634 ]
1635
1635
1636 $ hg version --config extensions.strip= -Tjson
1636 $ hg version --config extensions.strip= -Tjson
1637 [
1637 [
1638 {
1638 {
1639 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1639 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1640 "ver": "*" (glob)
1640 "ver": "*" (glob)
1641 }
1641 }
1642 ]
1642 ]
1643
1643
1644 Test template output of version:
1644 Test template output of version:
1645
1645
1646 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1646 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1647 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1647 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1648 strip (internal)
1648 strip (internal)
1649 throw 1.twentythree (external)
1649 throw 1.twentythree (external)
1650
1650
1651 Refuse to load extensions with minimum version requirements
1651 Refuse to load extensions with minimum version requirements
1652
1652
1653 $ cat > minversion1.py << EOF
1653 $ cat > minversion1.py << EOF
1654 > from mercurial import util
1654 > from mercurial import util
1655 > util.version = lambda: b'3.5.2'
1655 > util.version = lambda: b'3.5.2'
1656 > minimumhgversion = b'3.6'
1656 > minimumhgversion = b'3.6'
1657 > EOF
1657 > EOF
1658 $ hg --config extensions.minversion=minversion1.py version
1658 $ hg --config extensions.minversion=minversion1.py version
1659 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1659 (third party extension minversion requires version 3.6 or newer of Mercurial (current: 3.5.2); disabling)
1660 Mercurial Distributed SCM (version 3.5.2)
1660 Mercurial Distributed SCM (version 3.5.2)
1661 (see https://mercurial-scm.org for more information)
1661 (see https://mercurial-scm.org for more information)
1662
1662
1663 Copyright (C) 2005-* Olivia Mackall and others (glob)
1663 Copyright (C) 2005-* Olivia Mackall and others (glob)
1664 This is free software; see the source for copying conditions. There is NO
1664 This is free software; see the source for copying conditions. There is NO
1665 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1665 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1666
1666
1667 $ cat > minversion2.py << EOF
1667 $ cat > minversion2.py << EOF
1668 > from mercurial import util
1668 > from mercurial import util
1669 > util.version = lambda: b'3.6'
1669 > util.version = lambda: b'3.6'
1670 > minimumhgversion = b'3.7'
1670 > minimumhgversion = b'3.7'
1671 > EOF
1671 > EOF
1672 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1672 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1673 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1673 (third party extension minversion requires version 3.7 or newer of Mercurial (current: 3.6); disabling)
1674
1674
1675 Can load version that is only off by point release
1675 Can load version that is only off by point release
1676
1676
1677 $ cat > minversion2.py << EOF
1677 $ cat > minversion2.py << EOF
1678 > from mercurial import util
1678 > from mercurial import util
1679 > util.version = lambda: b'3.6.1'
1679 > util.version = lambda: b'3.6.1'
1680 > minimumhgversion = b'3.6'
1680 > minimumhgversion = b'3.6'
1681 > EOF
1681 > EOF
1682 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1682 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1683 [1]
1683 [1]
1684
1684
1685 Can load minimum version identical to current
1685 Can load minimum version identical to current
1686
1686
1687 $ cat > minversion3.py << EOF
1687 $ cat > minversion3.py << EOF
1688 > from mercurial import util
1688 > from mercurial import util
1689 > util.version = lambda: b'3.5'
1689 > util.version = lambda: b'3.5'
1690 > minimumhgversion = b'3.5'
1690 > minimumhgversion = b'3.5'
1691 > EOF
1691 > EOF
1692 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1692 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1693 [1]
1693 [1]
1694
1694
1695 Don't explode on py3 with a bad version number
1695 Don't explode on py3 with a bad version number (both str vs bytes, and not enough
1696 parts)
1696
1697
1697 $ cat > minversion4.py << EOF
1698 $ cat > minversion4.py << EOF
1698 > from mercurial import util
1699 > from mercurial import util
1699 > util.version = lambda: b'3.5'
1700 > util.version = lambda: b'3.5'
1700 > minimumhgversion = b'3'
1701 > minimumhgversion = '3'
1701 > EOF
1702 > EOF
1702 $ hg --config extensions.minversion=minversion4.py version -v
1703 $ hg --config extensions.minversion=minversion4.py version -v
1703 Mercurial Distributed SCM (version 3.5)
1704 Mercurial Distributed SCM (version 3.5)
1704 (see https://mercurial-scm.org for more information)
1705 (see https://mercurial-scm.org for more information)
1705
1706
1706 Copyright (C) 2005-* Olivia Mackall and others (glob)
1707 Copyright (C) 2005-* Olivia Mackall and others (glob)
1707 This is free software; see the source for copying conditions. There is NO
1708 This is free software; see the source for copying conditions. There is NO
1708 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1709 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1709
1710
1710 Enabled extensions:
1711 Enabled extensions:
1711
1712
1712 minversion external
1713 minversion external
1713
1714
1714 Restore HGRCPATH
1715 Restore HGRCPATH
1715
1716
1716 $ HGRCPATH=$ORGHGRCPATH
1717 $ HGRCPATH=$ORGHGRCPATH
1717 $ export HGRCPATH
1718 $ export HGRCPATH
1718
1719
1719 Commands handling multiple repositories at a time should invoke only
1720 Commands handling multiple repositories at a time should invoke only
1720 "reposetup()" of extensions enabling in the target repository.
1721 "reposetup()" of extensions enabling in the target repository.
1721
1722
1722 $ mkdir reposetup-test
1723 $ mkdir reposetup-test
1723 $ cd reposetup-test
1724 $ cd reposetup-test
1724
1725
1725 $ cat > $TESTTMP/reposetuptest.py <<EOF
1726 $ cat > $TESTTMP/reposetuptest.py <<EOF
1726 > from mercurial import extensions
1727 > from mercurial import extensions
1727 > def reposetup(ui, repo):
1728 > def reposetup(ui, repo):
1728 > ui.write(b'reposetup() for %s\n' % (repo.root))
1729 > ui.write(b'reposetup() for %s\n' % (repo.root))
1729 > ui.flush()
1730 > ui.flush()
1730 > EOF
1731 > EOF
1731 $ hg init src
1732 $ hg init src
1732 $ echo a > src/a
1733 $ echo a > src/a
1733 $ hg -R src commit -Am '#0 at src/a'
1734 $ hg -R src commit -Am '#0 at src/a'
1734 adding a
1735 adding a
1735 $ echo '[extensions]' >> src/.hg/hgrc
1736 $ echo '[extensions]' >> src/.hg/hgrc
1736 $ echo '# enable extension locally' >> src/.hg/hgrc
1737 $ echo '# enable extension locally' >> src/.hg/hgrc
1737 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1738 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1738 $ hg -R src status
1739 $ hg -R src status
1739 reposetup() for $TESTTMP/reposetup-test/src
1740 reposetup() for $TESTTMP/reposetup-test/src
1740 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1741 reposetup() for $TESTTMP/reposetup-test/src (chg !)
1741
1742
1742 #if no-extraextensions
1743 #if no-extraextensions
1743 $ hg --cwd src debugextensions
1744 $ hg --cwd src debugextensions
1744 reposetup() for $TESTTMP/reposetup-test/src
1745 reposetup() for $TESTTMP/reposetup-test/src
1745 dodo (untested!)
1746 dodo (untested!)
1746 dudu (untested!)
1747 dudu (untested!)
1747 mq
1748 mq
1748 reposetuptest (untested!)
1749 reposetuptest (untested!)
1749 strip
1750 strip
1750 #endif
1751 #endif
1751
1752
1752 $ hg clone -U src clone-dst1
1753 $ hg clone -U src clone-dst1
1753 reposetup() for $TESTTMP/reposetup-test/src
1754 reposetup() for $TESTTMP/reposetup-test/src
1754 $ hg init push-dst1
1755 $ hg init push-dst1
1755 $ hg -q -R src push push-dst1
1756 $ hg -q -R src push push-dst1
1756 reposetup() for $TESTTMP/reposetup-test/src
1757 reposetup() for $TESTTMP/reposetup-test/src
1757 $ hg init pull-src1
1758 $ hg init pull-src1
1758 $ hg -q -R pull-src1 pull src
1759 $ hg -q -R pull-src1 pull src
1759 reposetup() for $TESTTMP/reposetup-test/src
1760 reposetup() for $TESTTMP/reposetup-test/src
1760
1761
1761 $ cat <<EOF >> $HGRCPATH
1762 $ cat <<EOF >> $HGRCPATH
1762 > [extensions]
1763 > [extensions]
1763 > # disable extension globally and explicitly
1764 > # disable extension globally and explicitly
1764 > reposetuptest = !
1765 > reposetuptest = !
1765 > EOF
1766 > EOF
1766 $ hg clone -U src clone-dst2
1767 $ hg clone -U src clone-dst2
1767 reposetup() for $TESTTMP/reposetup-test/src
1768 reposetup() for $TESTTMP/reposetup-test/src
1768 $ hg init push-dst2
1769 $ hg init push-dst2
1769 $ hg -q -R src push push-dst2
1770 $ hg -q -R src push push-dst2
1770 reposetup() for $TESTTMP/reposetup-test/src
1771 reposetup() for $TESTTMP/reposetup-test/src
1771 $ hg init pull-src2
1772 $ hg init pull-src2
1772 $ hg -q -R pull-src2 pull src
1773 $ hg -q -R pull-src2 pull src
1773 reposetup() for $TESTTMP/reposetup-test/src
1774 reposetup() for $TESTTMP/reposetup-test/src
1774
1775
1775 $ cat <<EOF >> $HGRCPATH
1776 $ cat <<EOF >> $HGRCPATH
1776 > [extensions]
1777 > [extensions]
1777 > # enable extension globally
1778 > # enable extension globally
1778 > reposetuptest = $TESTTMP/reposetuptest.py
1779 > reposetuptest = $TESTTMP/reposetuptest.py
1779 > EOF
1780 > EOF
1780 $ hg clone -U src clone-dst3
1781 $ hg clone -U src clone-dst3
1781 reposetup() for $TESTTMP/reposetup-test/src
1782 reposetup() for $TESTTMP/reposetup-test/src
1782 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1783 reposetup() for $TESTTMP/reposetup-test/clone-dst3
1783 $ hg init push-dst3
1784 $ hg init push-dst3
1784 reposetup() for $TESTTMP/reposetup-test/push-dst3
1785 reposetup() for $TESTTMP/reposetup-test/push-dst3
1785 $ hg -q -R src push push-dst3
1786 $ hg -q -R src push push-dst3
1786 reposetup() for $TESTTMP/reposetup-test/src
1787 reposetup() for $TESTTMP/reposetup-test/src
1787 reposetup() for $TESTTMP/reposetup-test/push-dst3
1788 reposetup() for $TESTTMP/reposetup-test/push-dst3
1788 $ hg init pull-src3
1789 $ hg init pull-src3
1789 reposetup() for $TESTTMP/reposetup-test/pull-src3
1790 reposetup() for $TESTTMP/reposetup-test/pull-src3
1790 $ hg -q -R pull-src3 pull src
1791 $ hg -q -R pull-src3 pull src
1791 reposetup() for $TESTTMP/reposetup-test/pull-src3
1792 reposetup() for $TESTTMP/reposetup-test/pull-src3
1792 reposetup() for $TESTTMP/reposetup-test/src
1793 reposetup() for $TESTTMP/reposetup-test/src
1793
1794
1794 $ echo '[extensions]' >> src/.hg/hgrc
1795 $ echo '[extensions]' >> src/.hg/hgrc
1795 $ echo '# disable extension locally' >> src/.hg/hgrc
1796 $ echo '# disable extension locally' >> src/.hg/hgrc
1796 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1797 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1797 $ hg clone -U src clone-dst4
1798 $ hg clone -U src clone-dst4
1798 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1799 reposetup() for $TESTTMP/reposetup-test/clone-dst4
1799 $ hg init push-dst4
1800 $ hg init push-dst4
1800 reposetup() for $TESTTMP/reposetup-test/push-dst4
1801 reposetup() for $TESTTMP/reposetup-test/push-dst4
1801 $ hg -q -R src push push-dst4
1802 $ hg -q -R src push push-dst4
1802 reposetup() for $TESTTMP/reposetup-test/push-dst4
1803 reposetup() for $TESTTMP/reposetup-test/push-dst4
1803 $ hg init pull-src4
1804 $ hg init pull-src4
1804 reposetup() for $TESTTMP/reposetup-test/pull-src4
1805 reposetup() for $TESTTMP/reposetup-test/pull-src4
1805 $ hg -q -R pull-src4 pull src
1806 $ hg -q -R pull-src4 pull src
1806 reposetup() for $TESTTMP/reposetup-test/pull-src4
1807 reposetup() for $TESTTMP/reposetup-test/pull-src4
1807
1808
1808 disabling in command line overlays with all configuration
1809 disabling in command line overlays with all configuration
1809 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1810 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1810 $ hg --config extensions.reposetuptest=! init push-dst5
1811 $ hg --config extensions.reposetuptest=! init push-dst5
1811 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1812 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1812 $ hg --config extensions.reposetuptest=! init pull-src5
1813 $ hg --config extensions.reposetuptest=! init pull-src5
1813 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1814 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1814
1815
1815 $ cat <<EOF >> $HGRCPATH
1816 $ cat <<EOF >> $HGRCPATH
1816 > [extensions]
1817 > [extensions]
1817 > # disable extension globally and explicitly
1818 > # disable extension globally and explicitly
1818 > reposetuptest = !
1819 > reposetuptest = !
1819 > EOF
1820 > EOF
1820 $ hg init parent
1821 $ hg init parent
1821 $ hg init parent/sub1
1822 $ hg init parent/sub1
1822 $ echo 1 > parent/sub1/1
1823 $ echo 1 > parent/sub1/1
1823 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1824 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1824 adding 1
1825 adding 1
1825 $ hg init parent/sub2
1826 $ hg init parent/sub2
1826 $ hg init parent/sub2/sub21
1827 $ hg init parent/sub2/sub21
1827 $ echo 21 > parent/sub2/sub21/21
1828 $ echo 21 > parent/sub2/sub21/21
1828 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1829 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1829 adding 21
1830 adding 21
1830 $ cat > parent/sub2/.hgsub <<EOF
1831 $ cat > parent/sub2/.hgsub <<EOF
1831 > sub21 = sub21
1832 > sub21 = sub21
1832 > EOF
1833 > EOF
1833 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1834 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1834 adding .hgsub
1835 adding .hgsub
1835 $ hg init parent/sub3
1836 $ hg init parent/sub3
1836 $ echo 3 > parent/sub3/3
1837 $ echo 3 > parent/sub3/3
1837 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1838 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1838 adding 3
1839 adding 3
1839 $ cat > parent/.hgsub <<EOF
1840 $ cat > parent/.hgsub <<EOF
1840 > sub1 = sub1
1841 > sub1 = sub1
1841 > sub2 = sub2
1842 > sub2 = sub2
1842 > sub3 = sub3
1843 > sub3 = sub3
1843 > EOF
1844 > EOF
1844 $ hg -R parent commit -Am '#0 at parent'
1845 $ hg -R parent commit -Am '#0 at parent'
1845 adding .hgsub
1846 adding .hgsub
1846 $ echo '[extensions]' >> parent/.hg/hgrc
1847 $ echo '[extensions]' >> parent/.hg/hgrc
1847 $ echo '# enable extension locally' >> parent/.hg/hgrc
1848 $ echo '# enable extension locally' >> parent/.hg/hgrc
1848 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1849 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1849 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1850 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1850 $ hg -R parent status -S -A
1851 $ hg -R parent status -S -A
1851 reposetup() for $TESTTMP/reposetup-test/parent
1852 reposetup() for $TESTTMP/reposetup-test/parent
1852 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1853 reposetup() for $TESTTMP/reposetup-test/parent/sub2
1853 C .hgsub
1854 C .hgsub
1854 C .hgsubstate
1855 C .hgsubstate
1855 C sub1/1
1856 C sub1/1
1856 C sub2/.hgsub
1857 C sub2/.hgsub
1857 C sub2/.hgsubstate
1858 C sub2/.hgsubstate
1858 C sub2/sub21/21
1859 C sub2/sub21/21
1859 C sub3/3
1860 C sub3/3
1860
1861
1861 $ cd ..
1862 $ cd ..
1862
1863
1863 Prohibit registration of commands that don't use @command (issue5137)
1864 Prohibit registration of commands that don't use @command (issue5137)
1864
1865
1865 $ hg init deprecated
1866 $ hg init deprecated
1866 $ cd deprecated
1867 $ cd deprecated
1867
1868
1868 $ cat <<EOF > deprecatedcmd.py
1869 $ cat <<EOF > deprecatedcmd.py
1869 > def deprecatedcmd(repo, ui):
1870 > def deprecatedcmd(repo, ui):
1870 > pass
1871 > pass
1871 > cmdtable = {
1872 > cmdtable = {
1872 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1873 > b'deprecatedcmd': (deprecatedcmd, [], b''),
1873 > }
1874 > }
1874 > EOF
1875 > EOF
1875 $ cat <<EOF > .hg/hgrc
1876 $ cat <<EOF > .hg/hgrc
1876 > [extensions]
1877 > [extensions]
1877 > deprecatedcmd = `pwd`/deprecatedcmd.py
1878 > deprecatedcmd = `pwd`/deprecatedcmd.py
1878 > mq = !
1879 > mq = !
1879 > hgext.mq = !
1880 > hgext.mq = !
1880 > hgext/mq = !
1881 > hgext/mq = !
1881 > EOF
1882 > EOF
1882
1883
1883 $ hg deprecatedcmd > /dev/null
1884 $ hg deprecatedcmd > /dev/null
1884 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1885 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1885 *** (use @command decorator to register 'deprecatedcmd')
1886 *** (use @command decorator to register 'deprecatedcmd')
1886 hg: unknown command 'deprecatedcmd'
1887 hg: unknown command 'deprecatedcmd'
1887 (use 'hg help' for a list of commands)
1888 (use 'hg help' for a list of commands)
1888 [10]
1889 [10]
1889
1890
1890 the extension shouldn't be loaded at all so the mq works:
1891 the extension shouldn't be loaded at all so the mq works:
1891
1892
1892 $ hg qseries --config extensions.mq= > /dev/null
1893 $ hg qseries --config extensions.mq= > /dev/null
1893 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1894 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1894 *** (use @command decorator to register 'deprecatedcmd')
1895 *** (use @command decorator to register 'deprecatedcmd')
1895
1896
1896 $ cd ..
1897 $ cd ..
1897
1898
1898 Test synopsis and docstring extending
1899 Test synopsis and docstring extending
1899
1900
1900 $ hg init exthelp
1901 $ hg init exthelp
1901 $ cat > exthelp.py <<EOF
1902 $ cat > exthelp.py <<EOF
1902 > from mercurial import commands, extensions
1903 > from mercurial import commands, extensions
1903 > def exbookmarks(orig, *args, **opts):
1904 > def exbookmarks(orig, *args, **opts):
1904 > return orig(*args, **opts)
1905 > return orig(*args, **opts)
1905 > def uisetup(ui):
1906 > def uisetup(ui):
1906 > synopsis = b' GREPME [--foo] [-x]'
1907 > synopsis = b' GREPME [--foo] [-x]'
1907 > docstring = '''
1908 > docstring = '''
1908 > GREPME make sure that this is in the help!
1909 > GREPME make sure that this is in the help!
1909 > '''
1910 > '''
1910 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1911 > extensions.wrapcommand(commands.table, b'bookmarks', exbookmarks,
1911 > synopsis, docstring)
1912 > synopsis, docstring)
1912 > EOF
1913 > EOF
1913 $ abspath=`pwd`/exthelp.py
1914 $ abspath=`pwd`/exthelp.py
1914 $ echo '[extensions]' >> $HGRCPATH
1915 $ echo '[extensions]' >> $HGRCPATH
1915 $ echo "exthelp = $abspath" >> $HGRCPATH
1916 $ echo "exthelp = $abspath" >> $HGRCPATH
1916 $ cd exthelp
1917 $ cd exthelp
1917 $ hg help bookmarks | grep GREPME
1918 $ hg help bookmarks | grep GREPME
1918 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1919 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1919 GREPME make sure that this is in the help!
1920 GREPME make sure that this is in the help!
1920 $ cd ..
1921 $ cd ..
1921
1922
1922 Prohibit the use of unicode strings as the default value of options
1923 Prohibit the use of unicode strings as the default value of options
1923
1924
1924 $ hg init $TESTTMP/opt-unicode-default
1925 $ hg init $TESTTMP/opt-unicode-default
1925
1926
1926 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1927 $ cat > $TESTTMP/test_unicode_default_value.py << EOF
1927 > from __future__ import print_function
1928 > from __future__ import print_function
1928 > from mercurial import registrar
1929 > from mercurial import registrar
1929 > cmdtable = {}
1930 > cmdtable = {}
1930 > command = registrar.command(cmdtable)
1931 > command = registrar.command(cmdtable)
1931 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1932 > @command(b'dummy', [(b'', b'opt', u'value', u'help')], 'ext [OPTIONS]')
1932 > def ext(*args, **opts):
1933 > def ext(*args, **opts):
1933 > print(opts[b'opt'], flush=True)
1934 > print(opts[b'opt'], flush=True)
1934 > EOF
1935 > EOF
1935 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1936 $ "$PYTHON" $TESTTMP/unflush.py $TESTTMP/test_unicode_default_value.py
1936 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1937 $ cat > $TESTTMP/opt-unicode-default/.hg/hgrc << EOF
1937 > [extensions]
1938 > [extensions]
1938 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1939 > test_unicode_default_value = $TESTTMP/test_unicode_default_value.py
1939 > EOF
1940 > EOF
1940 $ hg -R $TESTTMP/opt-unicode-default dummy
1941 $ hg -R $TESTTMP/opt-unicode-default dummy
1941 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1942 *** failed to import extension test_unicode_default_value from $TESTTMP/test_unicode_default_value.py: unicode *'value' found in cmdtable.dummy (glob)
1942 *** (use b'' to make it byte string)
1943 *** (use b'' to make it byte string)
1943 hg: unknown command 'dummy'
1944 hg: unknown command 'dummy'
1944 (did you mean summary?)
1945 (did you mean summary?)
1945 [10]
1946 [10]
General Comments 0
You need to be logged in to leave comments. Login now