##// END OF EJS Templates
demandimport: prefer loaded module over package attribute (issue5617)...
Yuya Nishihara -
r33531:9cbbf911 default
parent child Browse files
Show More
@@ -1,302 +1,306 b''
1 1 # demandimport.py - global demand-loading of modules for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''
9 9 demandimport - automatic demandloading of modules
10 10
11 11 To enable this module, do:
12 12
13 13 import demandimport; demandimport.enable()
14 14
15 15 Imports of the following forms will be demand-loaded:
16 16
17 17 import a, b.c
18 18 import a.b as c
19 19 from a import b,c # a will be loaded immediately
20 20
21 21 These imports will not be delayed:
22 22
23 23 from a import *
24 24 b = __import__(a)
25 25 '''
26 26
27 27 from __future__ import absolute_import
28 28
29 29 import __builtin__ as builtins
30 30 import contextlib
31 31 import os
32 32 import sys
33 33
34 34 contextmanager = contextlib.contextmanager
35 35
36 36 _origimport = __import__
37 37
38 38 nothing = object()
39 39
40 40 def _hgextimport(importfunc, name, globals, *args, **kwargs):
41 41 try:
42 42 return importfunc(name, globals, *args, **kwargs)
43 43 except ImportError:
44 44 if not globals:
45 45 raise
46 46 # extensions are loaded with "hgext_" prefix
47 47 hgextname = 'hgext_%s' % name
48 48 nameroot = hgextname.split('.', 1)[0]
49 49 contextroot = globals.get('__name__', '').split('.', 1)[0]
50 50 if nameroot != contextroot:
51 51 raise
52 52 # retry to import with "hgext_" prefix
53 53 return importfunc(hgextname, globals, *args, **kwargs)
54 54
55 55 class _demandmod(object):
56 56 """module demand-loader and proxy
57 57
58 58 Specify 1 as 'level' argument at construction, to import module
59 59 relatively.
60 60 """
61 61
62 62 def __init__(self, name, globals, locals, level):
63 63 if '.' in name:
64 64 head, rest = name.split('.', 1)
65 65 after = [rest]
66 66 else:
67 67 head = name
68 68 after = []
69 69 object.__setattr__(self, r"_data",
70 70 (head, globals, locals, after, level, set()))
71 71 object.__setattr__(self, r"_module", None)
72 72
73 73 def _extend(self, name):
74 74 """add to the list of submodules to load"""
75 75 self._data[3].append(name)
76 76
77 77 def _addref(self, name):
78 78 """Record that the named module ``name`` imports this module.
79 79
80 80 References to this proxy class having the name of this module will be
81 81 replaced at module load time. We assume the symbol inside the importing
82 82 module is identical to the "head" name of this module. We don't
83 83 actually know if "as X" syntax is being used to change the symbol name
84 84 because this information isn't exposed to __import__.
85 85 """
86 86 self._data[5].add(name)
87 87
88 88 def _load(self):
89 89 if not self._module:
90 90 head, globals, locals, after, level, modrefs = self._data
91 91 mod = _hgextimport(_origimport, head, globals, locals, None, level)
92 92 if mod is self:
93 93 # In this case, _hgextimport() above should imply
94 94 # _demandimport(). Otherwise, _hgextimport() never
95 95 # returns _demandmod. This isn't intentional behavior,
96 96 # in fact. (see also issue5304 for detail)
97 97 #
98 98 # If self._module is already bound at this point, self
99 99 # should be already _load()-ed while _hgextimport().
100 100 # Otherwise, there is no way to import actual module
101 101 # as expected, because (re-)invoking _hgextimport()
102 102 # should cause same result.
103 103 # This is reason why _load() returns without any more
104 104 # setup but assumes self to be already bound.
105 105 mod = self._module
106 106 assert mod and mod is not self, "%s, %s" % (self, mod)
107 107 return
108 108
109 109 # load submodules
110 110 def subload(mod, p):
111 111 h, t = p, None
112 112 if '.' in p:
113 113 h, t = p.split('.', 1)
114 114 if getattr(mod, h, nothing) is nothing:
115 115 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__,
116 116 level=1))
117 117 elif t:
118 118 subload(getattr(mod, h), t)
119 119
120 120 for x in after:
121 121 subload(mod, x)
122 122
123 123 # Replace references to this proxy instance with the actual module.
124 124 if locals:
125 125 if locals.get(head) is self:
126 126 locals[head] = mod
127 127 elif locals.get(head + r'mod') is self:
128 128 locals[head + r'mod'] = mod
129 129
130 130 for modname in modrefs:
131 131 modref = sys.modules.get(modname, None)
132 132 if modref and getattr(modref, head, None) is self:
133 133 setattr(modref, head, mod)
134 134
135 135 object.__setattr__(self, r"_module", mod)
136 136
137 137 def __repr__(self):
138 138 if self._module:
139 139 return "<proxied module '%s'>" % self._data[0]
140 140 return "<unloaded module '%s'>" % self._data[0]
141 141
142 142 def __call__(self, *args, **kwargs):
143 143 raise TypeError("%s object is not callable" % repr(self))
144 144
145 145 def __getattr__(self, attr):
146 146 self._load()
147 147 return getattr(self._module, attr)
148 148
149 149 def __setattr__(self, attr, val):
150 150 self._load()
151 151 setattr(self._module, attr, val)
152 152
153 153 @property
154 154 def __dict__(self):
155 155 self._load()
156 156 return self._module.__dict__
157 157
158 158 @property
159 159 def __doc__(self):
160 160 self._load()
161 161 return self._module.__doc__
162 162
163 163 _pypy = '__pypy__' in sys.builtin_module_names
164 164
165 165 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
166 166 if locals is None or name in ignore or fromlist == ('*',):
167 167 # these cases we can't really delay
168 168 return _hgextimport(_origimport, name, globals, locals, fromlist, level)
169 169 elif not fromlist:
170 170 # import a [as b]
171 171 if '.' in name: # a.b
172 172 base, rest = name.split('.', 1)
173 173 # email.__init__ loading email.mime
174 174 if globals and globals.get('__name__', None) == base:
175 175 return _origimport(name, globals, locals, fromlist, level)
176 176 # if a is already demand-loaded, add b to its submodule list
177 177 if base in locals:
178 178 if isinstance(locals[base], _demandmod):
179 179 locals[base]._extend(rest)
180 180 return locals[base]
181 181 return _demandmod(name, globals, locals, level)
182 182 else:
183 183 # There is a fromlist.
184 184 # from a import b,c,d
185 185 # from . import b,c,d
186 186 # from .a import b,c,d
187 187
188 188 # level == -1: relative and absolute attempted (Python 2 only).
189 189 # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
190 190 # The modern Mercurial convention is to use absolute_import everywhere,
191 191 # so modern Mercurial code will have level >= 0.
192 192
193 193 # The name of the module the import statement is located in.
194 194 globalname = globals.get('__name__')
195 195
196 196 def processfromitem(mod, attr):
197 197 """Process an imported symbol in the import statement.
198 198
199 199 If the symbol doesn't exist in the parent module, and if the
200 200 parent module is a package, it must be a module. We set missing
201 201 modules up as _demandmod instances.
202 202 """
203 203 symbol = getattr(mod, attr, nothing)
204 204 nonpkg = getattr(mod, '__path__', nothing) is nothing
205 205 if symbol is nothing:
206 206 if nonpkg:
207 207 # do not try relative import, which would raise ValueError,
208 208 # and leave unknown attribute as the default __import__()
209 209 # would do. the missing attribute will be detected later
210 210 # while processing the import statement.
211 211 return
212 212 mn = '%s.%s' % (mod.__name__, attr)
213 213 if mn in ignore:
214 214 importfunc = _origimport
215 215 else:
216 216 importfunc = _demandmod
217 217 symbol = importfunc(attr, mod.__dict__, locals, level=1)
218 218 setattr(mod, attr, symbol)
219 219
220 220 # Record the importing module references this symbol so we can
221 221 # replace the symbol with the actual module instance at load
222 222 # time.
223 223 if globalname and isinstance(symbol, _demandmod):
224 224 symbol._addref(globalname)
225 225
226 226 def chainmodules(rootmod, modname):
227 227 # recurse down the module chain, and return the leaf module
228 228 mod = rootmod
229 229 for comp in modname.split('.')[1:]:
230 if getattr(mod, comp, nothing) is nothing:
231 setattr(mod, comp, _demandmod(comp, mod.__dict__,
232 mod.__dict__, level=1))
233 mod = getattr(mod, comp)
230 obj = getattr(mod, comp, nothing)
231 if obj is nothing:
232 obj = _demandmod(comp, mod.__dict__, mod.__dict__, level=1)
233 setattr(mod, comp, obj)
234 elif mod.__name__ + '.' + comp in sys.modules:
235 # prefer loaded module over attribute (issue5617)
236 obj = sys.modules[mod.__name__ + '.' + comp]
237 mod = obj
234 238 return mod
235 239
236 240 if level >= 0:
237 241 if name:
238 242 # "from a import b" or "from .a import b" style
239 243 rootmod = _hgextimport(_origimport, name, globals, locals,
240 244 level=level)
241 245 mod = chainmodules(rootmod, name)
242 246 elif _pypy:
243 247 # PyPy's __import__ throws an exception if invoked
244 248 # with an empty name and no fromlist. Recreate the
245 249 # desired behaviour by hand.
246 250 mn = globalname
247 251 mod = sys.modules[mn]
248 252 if getattr(mod, '__path__', nothing) is nothing:
249 253 mn = mn.rsplit('.', 1)[0]
250 254 mod = sys.modules[mn]
251 255 if level > 1:
252 256 mn = mn.rsplit('.', level - 1)[0]
253 257 mod = sys.modules[mn]
254 258 else:
255 259 mod = _hgextimport(_origimport, name, globals, locals,
256 260 level=level)
257 261
258 262 for x in fromlist:
259 263 processfromitem(mod, x)
260 264
261 265 return mod
262 266
263 267 # But, we still need to support lazy loading of standard library and 3rd
264 268 # party modules. So handle level == -1.
265 269 mod = _hgextimport(_origimport, name, globals, locals)
266 270 mod = chainmodules(mod, name)
267 271
268 272 for x in fromlist:
269 273 processfromitem(mod, x)
270 274
271 275 return mod
272 276
273 277 ignore = []
274 278
275 279 def init(ignorelist):
276 280 global ignore
277 281 ignore = ignorelist
278 282
279 283 def isenabled():
280 284 return builtins.__import__ == _demandimport
281 285
282 286 def enable():
283 287 "enable global demand-loading of modules"
284 288 if os.environ.get('HGDEMANDIMPORT') != 'disable':
285 289 builtins.__import__ = _demandimport
286 290
287 291 def disable():
288 292 "disable global demand-loading of modules"
289 293 builtins.__import__ = _origimport
290 294
291 295 @contextmanager
292 296 def deactivated():
293 297 "context manager for disabling demandimport in 'with' blocks"
294 298 demandenabled = isenabled()
295 299 if demandenabled:
296 300 disable()
297 301
298 302 try:
299 303 yield
300 304 finally:
301 305 if demandenabled:
302 306 enable()
@@ -1,1685 +1,1706 b''
1 1 Test basic extension support
2 2
3 3 $ cat > foobar.py <<EOF
4 4 > import os
5 5 > from mercurial import commands, registrar
6 6 > cmdtable = {}
7 7 > command = registrar.command(cmdtable)
8 8 > configtable = {}
9 9 > configitem = registrar.configitem(configtable)
10 10 > configitem('tests', 'foo', default="Foo")
11 11 > def uisetup(ui):
12 12 > ui.write("uisetup called\\n")
13 13 > ui.flush()
14 14 > def reposetup(ui, repo):
15 15 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
16 16 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
17 17 > ui.flush()
18 18 > @command(b'foo', [], 'hg foo')
19 19 > def foo(ui, *args, **kwargs):
20 20 > foo = ui.config('tests', 'foo')
21 21 > ui.write(foo)
22 22 > ui.write("\\n")
23 23 > @command(b'bar', [], 'hg bar', norepo=True)
24 24 > def bar(ui, *args, **kwargs):
25 25 > ui.write("Bar\\n")
26 26 > EOF
27 27 $ abspath=`pwd`/foobar.py
28 28
29 29 $ mkdir barfoo
30 30 $ cp foobar.py barfoo/__init__.py
31 31 $ barfoopath=`pwd`/barfoo
32 32
33 33 $ hg init a
34 34 $ cd a
35 35 $ echo foo > file
36 36 $ hg add file
37 37 $ hg commit -m 'add file'
38 38
39 39 $ echo '[extensions]' >> $HGRCPATH
40 40 $ echo "foobar = $abspath" >> $HGRCPATH
41 41 $ hg foo
42 42 uisetup called
43 43 reposetup called for a
44 44 ui == repo.ui
45 45 Foo
46 46
47 47 $ cd ..
48 48 $ hg clone a b
49 49 uisetup called
50 50 reposetup called for a
51 51 ui == repo.ui
52 52 reposetup called for b
53 53 ui == repo.ui
54 54 updating to branch default
55 55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56
57 57 $ hg bar
58 58 uisetup called
59 59 Bar
60 60 $ echo 'foobar = !' >> $HGRCPATH
61 61
62 62 module/__init__.py-style
63 63
64 64 $ echo "barfoo = $barfoopath" >> $HGRCPATH
65 65 $ cd a
66 66 $ hg foo
67 67 uisetup called
68 68 reposetup called for a
69 69 ui == repo.ui
70 70 Foo
71 71 $ echo 'barfoo = !' >> $HGRCPATH
72 72
73 73 Check that extensions are loaded in phases:
74 74
75 75 $ cat > foo.py <<EOF
76 76 > import os
77 77 > name = os.path.basename(__file__).rsplit('.', 1)[0]
78 78 > print "1) %s imported" % name
79 79 > def uisetup(ui):
80 80 > print "2) %s uisetup" % name
81 81 > def extsetup():
82 82 > print "3) %s extsetup" % name
83 83 > def reposetup(ui, repo):
84 84 > print "4) %s reposetup" % name
85 85 >
86 86 > # custom predicate to check registration of functions at loading
87 87 > from mercurial import (
88 88 > registrar,
89 89 > smartset,
90 90 > )
91 91 > revsetpredicate = registrar.revsetpredicate()
92 92 > @revsetpredicate(name, safe=True) # safe=True for query via hgweb
93 93 > def custompredicate(repo, subset, x):
94 94 > return smartset.baseset([r for r in subset if r in {0}])
95 95 > EOF
96 96
97 97 $ cp foo.py bar.py
98 98 $ echo 'foo = foo.py' >> $HGRCPATH
99 99 $ echo 'bar = bar.py' >> $HGRCPATH
100 100
101 101 Check normal command's load order of extensions and registration of functions
102 102
103 103 $ hg log -r "foo() and bar()" -q
104 104 1) foo imported
105 105 1) bar imported
106 106 2) foo uisetup
107 107 2) bar uisetup
108 108 3) foo extsetup
109 109 3) bar extsetup
110 110 4) foo reposetup
111 111 4) bar reposetup
112 112 0:c24b9ac61126
113 113
114 114 Check hgweb's load order of extensions and registration of functions
115 115
116 116 $ cat > hgweb.cgi <<EOF
117 117 > #!$PYTHON
118 118 > from mercurial import demandimport; demandimport.enable()
119 119 > from mercurial.hgweb import hgweb
120 120 > from mercurial.hgweb import wsgicgi
121 121 > application = hgweb('.', 'test repo')
122 122 > wsgicgi.launch(application)
123 123 > EOF
124 124 $ . "$TESTDIR/cgienv"
125 125
126 126 $ PATH_INFO='/' SCRIPT_NAME='' $PYTHON hgweb.cgi \
127 127 > | grep '^[0-9]) ' # ignores HTML output
128 128 1) foo imported
129 129 1) bar imported
130 130 2) foo uisetup
131 131 2) bar uisetup
132 132 3) foo extsetup
133 133 3) bar extsetup
134 134 4) foo reposetup
135 135 4) bar reposetup
136 136
137 137 (check that revset predicate foo() and bar() are available)
138 138
139 139 #if msys
140 140 $ PATH_INFO='//shortlog'
141 141 #else
142 142 $ PATH_INFO='/shortlog'
143 143 #endif
144 144 $ export PATH_INFO
145 145 $ SCRIPT_NAME='' QUERY_STRING='rev=foo() and bar()' $PYTHON hgweb.cgi \
146 146 > | grep '<a href="/rev/[0-9a-z]*">'
147 147 <a href="/rev/c24b9ac61126">add file</a>
148 148
149 149 $ echo 'foo = !' >> $HGRCPATH
150 150 $ echo 'bar = !' >> $HGRCPATH
151 151
152 152 Check "from __future__ import absolute_import" support for external libraries
153 153
154 154 #if windows
155 155 $ PATHSEP=";"
156 156 #else
157 157 $ PATHSEP=":"
158 158 #endif
159 159 $ export PATHSEP
160 160
161 161 $ mkdir $TESTTMP/libroot
162 162 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
163 163 $ mkdir $TESTTMP/libroot/mod
164 164 $ touch $TESTTMP/libroot/mod/__init__.py
165 165 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
166 166
167 167 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
168 168 > from __future__ import absolute_import
169 169 > import ambig # should load "libroot/ambig.py"
170 170 > s = ambig.s
171 171 > EOF
172 172 $ cat > loadabs.py <<EOF
173 173 > import mod.ambigabs as ambigabs
174 174 > def extsetup():
175 175 > print 'ambigabs.s=%s' % ambigabs.s
176 176 > EOF
177 177 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
178 178 ambigabs.s=libroot/ambig.py
179 179 $TESTTMP/a (glob)
180 180
181 181 #if no-py3k
182 182 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
183 183 > import ambig # should load "libroot/mod/ambig.py"
184 184 > s = ambig.s
185 185 > EOF
186 186 $ cat > loadrel.py <<EOF
187 187 > import mod.ambigrel as ambigrel
188 188 > def extsetup():
189 189 > print 'ambigrel.s=%s' % ambigrel.s
190 190 > EOF
191 191 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
192 192 ambigrel.s=libroot/mod/ambig.py
193 193 $TESTTMP/a (glob)
194 194 #endif
195 195
196 196 Check absolute/relative import of extension specific modules
197 197
198 198 $ mkdir $TESTTMP/extroot
199 199 $ cat > $TESTTMP/extroot/bar.py <<EOF
200 200 > s = 'this is extroot.bar'
201 201 > EOF
202 202 $ mkdir $TESTTMP/extroot/sub1
203 203 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
204 204 > s = 'this is extroot.sub1.__init__'
205 205 > EOF
206 206 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
207 207 > s = 'this is extroot.sub1.baz'
208 208 > EOF
209 209 $ cat > $TESTTMP/extroot/__init__.py <<EOF
210 210 > s = 'this is extroot.__init__'
211 211 > import foo
212 212 > def extsetup(ui):
213 213 > ui.write('(extroot) ', foo.func(), '\n')
214 214 > ui.flush()
215 215 > EOF
216 216
217 217 $ cat > $TESTTMP/extroot/foo.py <<EOF
218 218 > # test absolute import
219 219 > buf = []
220 220 > def func():
221 221 > # "not locals" case
222 222 > import extroot.bar
223 223 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
224 224 > return '\n(extroot) '.join(buf)
225 225 > # "fromlist == ('*',)" case
226 226 > from extroot.bar import *
227 227 > buf.append('from extroot.bar import *: %s' % s)
228 228 > # "not fromlist" and "if '.' in name" case
229 229 > import extroot.sub1.baz
230 230 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
231 231 > # "not fromlist" and NOT "if '.' in name" case
232 232 > import extroot
233 233 > buf.append('import extroot: %s' % extroot.s)
234 234 > # NOT "not fromlist" and NOT "level != -1" case
235 235 > from extroot.bar import s
236 236 > buf.append('from extroot.bar import s: %s' % s)
237 237 > EOF
238 238 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.extroot=$TESTTMP/extroot root)
239 239 (extroot) from extroot.bar import *: this is extroot.bar
240 240 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
241 241 (extroot) import extroot: this is extroot.__init__
242 242 (extroot) from extroot.bar import s: this is extroot.bar
243 243 (extroot) import extroot.bar in func(): this is extroot.bar
244 244 $TESTTMP/a (glob)
245 245
246 246 #if no-py3k
247 247 $ rm "$TESTTMP"/extroot/foo.*
248 248 $ cat > $TESTTMP/extroot/foo.py <<EOF
249 249 > # test relative import
250 250 > buf = []
251 251 > def func():
252 252 > # "not locals" case
253 253 > import bar
254 254 > buf.append('import bar in func(): %s' % bar.s)
255 255 > return '\n(extroot) '.join(buf)
256 256 > # "fromlist == ('*',)" case
257 257 > from bar import *
258 258 > buf.append('from bar import *: %s' % s)
259 259 > # "not fromlist" and "if '.' in name" case
260 260 > import sub1.baz
261 261 > buf.append('import sub1.baz: %s' % sub1.baz.s)
262 262 > # "not fromlist" and NOT "if '.' in name" case
263 263 > import sub1
264 264 > buf.append('import sub1: %s' % sub1.s)
265 265 > # NOT "not fromlist" and NOT "level != -1" case
266 266 > from bar import s
267 267 > buf.append('from bar import s: %s' % s)
268 268 > EOF
269 269 $ hg --config extensions.extroot=$TESTTMP/extroot root
270 270 (extroot) from bar import *: this is extroot.bar
271 271 (extroot) import sub1.baz: this is extroot.sub1.baz
272 272 (extroot) import sub1: this is extroot.sub1.__init__
273 273 (extroot) from bar import s: this is extroot.bar
274 274 (extroot) import bar in func(): this is extroot.bar
275 275 $TESTTMP/a (glob)
276 276 #endif
277 277
278 278 #if demandimport
279 279
280 280 Examine whether module loading is delayed until actual referring, even
281 281 though module is imported with "absolute_import" feature.
282 282
283 283 Files below in each packages are used for described purpose:
284 284
285 285 - "called": examine whether "from MODULE import ATTR" works correctly
286 286 - "unused": examine whether loading is delayed correctly
287 287 - "used": examine whether "from PACKAGE import MODULE" works correctly
288 288
289 289 Package hierarchy is needed to examine whether demand importing works
290 290 as expected for "from SUB.PACK.AGE import MODULE".
291 291
292 292 Setup "external library" to be imported with "absolute_import"
293 293 feature.
294 294
295 295 $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2
296 296 $ touch $TESTTMP/extlibroot/__init__.py
297 297 $ touch $TESTTMP/extlibroot/lsub1/__init__.py
298 298 $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py
299 299
300 300 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF
301 301 > def func():
302 302 > return "this is extlibroot.lsub1.lsub2.called.func()"
303 303 > EOF
304 304 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF
305 305 > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally")
306 306 > EOF
307 307 $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF
308 308 > detail = "this is extlibroot.lsub1.lsub2.used"
309 309 > EOF
310 310
311 311 Setup sub-package of "external library", which causes instantiation of
312 312 demandmod in "recurse down the module chain" code path. Relative
313 313 importing with "absolute_import" feature isn't tested, because "level
314 314 >=1 " doesn't cause instantiation of demandmod.
315 315
316 316 $ mkdir -p $TESTTMP/extlibroot/recursedown/abs
317 317 $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF
318 318 > detail = "this is extlibroot.recursedown.abs.used"
319 319 > EOF
320 320 $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF
321 321 > from __future__ import absolute_import
322 322 > from extlibroot.recursedown.abs.used import detail
323 323 > EOF
324 324
325 325 $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy
326 326 $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF
327 327 > detail = "this is extlibroot.recursedown.legacy.used"
328 328 > EOF
329 329 $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF
330 330 > # legacy style (level == -1) import
331 331 > from extlibroot.recursedown.legacy.used import detail
332 332 > EOF
333 333
334 334 $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF
335 335 > from __future__ import absolute_import
336 336 > from extlibroot.recursedown.abs import detail as absdetail
337 337 > from .legacy import detail as legacydetail
338 338 > EOF
339 339
340 Setup package that re-exports an attribute of its submodule as the same
341 name. This leaves 'shadowing.used' pointing to 'used.detail', but still
342 the submodule 'used' should be somehow accessible. (issue5617)
343
344 $ mkdir -p $TESTTMP/extlibroot/shadowing
345 $ cat > $TESTTMP/extlibroot/shadowing/used.py <<EOF
346 > detail = "this is extlibroot.shadowing.used"
347 > EOF
348 $ cat > $TESTTMP/extlibroot/shadowing/proxied.py <<EOF
349 > from __future__ import absolute_import
350 > from extlibroot.shadowing.used import detail
351 > EOF
352 $ cat > $TESTTMP/extlibroot/shadowing/__init__.py <<EOF
353 > from __future__ import absolute_import
354 > from .used import detail as used
355 > EOF
356
340 357 Setup extension local modules to be imported with "absolute_import"
341 358 feature.
342 359
343 360 $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2
344 361 $ touch $TESTTMP/absextroot/xsub1/__init__.py
345 362 $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py
346 363
347 364 $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF
348 365 > def func():
349 366 > return "this is absextroot.xsub1.xsub2.called.func()"
350 367 > EOF
351 368 $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF
352 369 > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally")
353 370 > EOF
354 371 $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF
355 372 > detail = "this is absextroot.xsub1.xsub2.used"
356 373 > EOF
357 374
358 375 Setup extension local modules to examine whether demand importing
359 376 works as expected in "level > 1" case.
360 377
361 378 $ cat > $TESTTMP/absextroot/relimportee.py <<EOF
362 379 > detail = "this is absextroot.relimportee"
363 380 > EOF
364 381 $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF
365 382 > from __future__ import absolute_import
366 383 > from ... import relimportee
367 384 > detail = "this relimporter imports %r" % (relimportee.detail)
368 385 > EOF
369 386
370 387 Setup modules, which actually import extension local modules at
371 388 runtime.
372 389
373 390 $ cat > $TESTTMP/absextroot/absolute.py << EOF
374 391 > from __future__ import absolute_import
375 392 >
376 393 > # import extension local modules absolutely (level = 0)
377 394 > from absextroot.xsub1.xsub2 import used, unused
378 395 > from absextroot.xsub1.xsub2.called import func
379 396 >
380 397 > def getresult():
381 398 > result = []
382 399 > result.append(used.detail)
383 400 > result.append(func())
384 401 > return result
385 402 > EOF
386 403
387 404 $ cat > $TESTTMP/absextroot/relative.py << EOF
388 405 > from __future__ import absolute_import
389 406 >
390 407 > # import extension local modules relatively (level == 1)
391 408 > from .xsub1.xsub2 import used, unused
392 409 > from .xsub1.xsub2.called import func
393 410 >
394 411 > # import a module, which implies "importing with level > 1"
395 412 > from .xsub1.xsub2 import relimporter
396 413 >
397 414 > def getresult():
398 415 > result = []
399 416 > result.append(used.detail)
400 417 > result.append(func())
401 418 > result.append(relimporter.detail)
402 419 > return result
403 420 > EOF
404 421
405 422 Setup main procedure of extension.
406 423
407 424 $ cat > $TESTTMP/absextroot/__init__.py <<EOF
408 425 > from __future__ import absolute_import
409 426 > from mercurial import registrar
410 427 > cmdtable = {}
411 428 > command = registrar.command(cmdtable)
412 429 >
413 430 > # "absolute" and "relative" shouldn't be imported before actual
414 431 > # command execution, because (1) they import same modules, and (2)
415 432 > # preceding import (= instantiate "demandmod" object instead of
416 433 > # real "module" object) might hide problem of succeeding import.
417 434 >
418 435 > @command(b'showabsolute', [], norepo=True)
419 436 > def showabsolute(ui, *args, **opts):
420 437 > from absextroot import absolute
421 438 > ui.write('ABS: %s\n' % '\nABS: '.join(absolute.getresult()))
422 439 >
423 440 > @command(b'showrelative', [], norepo=True)
424 441 > def showrelative(ui, *args, **opts):
425 442 > from . import relative
426 443 > ui.write('REL: %s\n' % '\nREL: '.join(relative.getresult()))
427 444 >
428 445 > # import modules from external library
429 446 > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused
430 447 > from extlibroot.lsub1.lsub2.called import func as lfunc
431 448 > from extlibroot.recursedown import absdetail, legacydetail
449 > from extlibroot.shadowing import proxied
432 450 >
433 451 > def uisetup(ui):
434 452 > result = []
435 453 > result.append(lused.detail)
436 454 > result.append(lfunc())
437 455 > result.append(absdetail)
438 456 > result.append(legacydetail)
457 > result.append(proxied.detail)
439 458 > ui.write('LIB: %s\n' % '\nLIB: '.join(result))
440 459 > EOF
441 460
442 461 Examine module importing.
443 462
444 463 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute)
445 464 LIB: this is extlibroot.lsub1.lsub2.used
446 465 LIB: this is extlibroot.lsub1.lsub2.called.func()
447 466 LIB: this is extlibroot.recursedown.abs.used
448 467 LIB: this is extlibroot.recursedown.legacy.used
468 LIB: this is extlibroot.shadowing.used
449 469 ABS: this is absextroot.xsub1.xsub2.used
450 470 ABS: this is absextroot.xsub1.xsub2.called.func()
451 471
452 472 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative)
453 473 LIB: this is extlibroot.lsub1.lsub2.used
454 474 LIB: this is extlibroot.lsub1.lsub2.called.func()
455 475 LIB: this is extlibroot.recursedown.abs.used
456 476 LIB: this is extlibroot.recursedown.legacy.used
477 LIB: this is extlibroot.shadowing.used
457 478 REL: this is absextroot.xsub1.xsub2.used
458 479 REL: this is absextroot.xsub1.xsub2.called.func()
459 480 REL: this relimporter imports 'this is absextroot.relimportee'
460 481
461 482 Examine whether sub-module is imported relatively as expected.
462 483
463 484 See also issue5208 for detail about example case on Python 3.x.
464 485
465 486 $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
466 487 $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
467 488
468 489 $ cat > $TESTTMP/notexist.py <<EOF
469 490 > text = 'notexist.py at root is loaded unintentionally\n'
470 491 > EOF
471 492
472 493 $ cat > $TESTTMP/checkrelativity.py <<EOF
473 494 > from mercurial import registrar
474 495 > cmdtable = {}
475 496 > command = registrar.command(cmdtable)
476 497 >
477 498 > # demand import avoids failure of importing notexist here
478 499 > import extlibroot.lsub1.lsub2.notexist
479 500 >
480 501 > @command(b'checkrelativity', [], norepo=True)
481 502 > def checkrelativity(ui, *args, **opts):
482 503 > try:
483 504 > ui.write(extlibroot.lsub1.lsub2.notexist.text)
484 505 > return 1 # unintentional success
485 506 > except ImportError:
486 507 > pass # intentional failure
487 508 > EOF
488 509
489 510 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
490 511
491 512 #endif
492 513
493 514 $ cd ..
494 515
495 516 hide outer repo
496 517 $ hg init
497 518
498 519 $ cat > empty.py <<EOF
499 520 > '''empty cmdtable
500 521 > '''
501 522 > cmdtable = {}
502 523 > EOF
503 524 $ emptypath=`pwd`/empty.py
504 525 $ echo "empty = $emptypath" >> $HGRCPATH
505 526 $ hg help empty
506 527 empty extension - empty cmdtable
507 528
508 529 no commands defined
509 530
510 531
511 532 $ echo 'empty = !' >> $HGRCPATH
512 533
513 534 $ cat > debugextension.py <<EOF
514 535 > '''only debugcommands
515 536 > '''
516 537 > from mercurial import registrar
517 538 > cmdtable = {}
518 539 > command = registrar.command(cmdtable)
519 540 > @command(b'debugfoobar', [], 'hg debugfoobar')
520 541 > def debugfoobar(ui, repo, *args, **opts):
521 542 > "yet another debug command"
522 543 > pass
523 544 > @command(b'foo', [], 'hg foo')
524 545 > def foo(ui, repo, *args, **opts):
525 546 > """yet another foo command
526 547 > This command has been DEPRECATED since forever.
527 548 > """
528 549 > pass
529 550 > EOF
530 551 $ debugpath=`pwd`/debugextension.py
531 552 $ echo "debugextension = $debugpath" >> $HGRCPATH
532 553
533 554 $ hg help debugextension
534 555 hg debugextensions
535 556
536 557 show information about active extensions
537 558
538 559 options:
539 560
540 561 (some details hidden, use --verbose to show complete help)
541 562
542 563
543 564 $ hg --verbose help debugextension
544 565 hg debugextensions
545 566
546 567 show information about active extensions
547 568
548 569 options:
549 570
550 571 -T --template TEMPLATE display with template (EXPERIMENTAL)
551 572
552 573 global options ([+] can be repeated):
553 574
554 575 -R --repository REPO repository root directory or name of overlay bundle
555 576 file
556 577 --cwd DIR change working directory
557 578 -y --noninteractive do not prompt, automatically pick the first choice for
558 579 all prompts
559 580 -q --quiet suppress output
560 581 -v --verbose enable additional output
561 582 --color TYPE when to colorize (boolean, always, auto, never, or
562 583 debug)
563 584 --config CONFIG [+] set/override config option (use 'section.name=value')
564 585 --debug enable debugging output
565 586 --debugger start debugger
566 587 --encoding ENCODE set the charset encoding (default: ascii)
567 588 --encodingmode MODE set the charset encoding mode (default: strict)
568 589 --traceback always print a traceback on exception
569 590 --time time how long the command takes
570 591 --profile print command execution profile
571 592 --version output version information and exit
572 593 -h --help display help and exit
573 594 --hidden consider hidden changesets
574 595 --pager TYPE when to paginate (boolean, always, auto, or never)
575 596 (default: auto)
576 597
577 598
578 599
579 600
580 601
581 602
582 603 $ hg --debug help debugextension
583 604 hg debugextensions
584 605
585 606 show information about active extensions
586 607
587 608 options:
588 609
589 610 -T --template TEMPLATE display with template (EXPERIMENTAL)
590 611
591 612 global options ([+] can be repeated):
592 613
593 614 -R --repository REPO repository root directory or name of overlay bundle
594 615 file
595 616 --cwd DIR change working directory
596 617 -y --noninteractive do not prompt, automatically pick the first choice for
597 618 all prompts
598 619 -q --quiet suppress output
599 620 -v --verbose enable additional output
600 621 --color TYPE when to colorize (boolean, always, auto, never, or
601 622 debug)
602 623 --config CONFIG [+] set/override config option (use 'section.name=value')
603 624 --debug enable debugging output
604 625 --debugger start debugger
605 626 --encoding ENCODE set the charset encoding (default: ascii)
606 627 --encodingmode MODE set the charset encoding mode (default: strict)
607 628 --traceback always print a traceback on exception
608 629 --time time how long the command takes
609 630 --profile print command execution profile
610 631 --version output version information and exit
611 632 -h --help display help and exit
612 633 --hidden consider hidden changesets
613 634 --pager TYPE when to paginate (boolean, always, auto, or never)
614 635 (default: auto)
615 636
616 637
617 638
618 639
619 640
620 641 $ echo 'debugextension = !' >> $HGRCPATH
621 642
622 643 Asking for help about a deprecated extension should do something useful:
623 644
624 645 $ hg help glog
625 646 'glog' is provided by the following extension:
626 647
627 648 graphlog command to view revision graphs from a shell (DEPRECATED)
628 649
629 650 (use 'hg help extensions' for information on enabling extensions)
630 651
631 652 Extension module help vs command help:
632 653
633 654 $ echo 'extdiff =' >> $HGRCPATH
634 655 $ hg help extdiff
635 656 hg extdiff [OPT]... [FILE]...
636 657
637 658 use external program to diff repository (or selected files)
638 659
639 660 Show differences between revisions for the specified files, using an
640 661 external program. The default program used is diff, with default options
641 662 "-Npru".
642 663
643 664 To select a different program, use the -p/--program option. The program
644 665 will be passed the names of two directories to compare. To pass additional
645 666 options to the program, use -o/--option. These will be passed before the
646 667 names of the directories to compare.
647 668
648 669 When two revision arguments are given, then changes are shown between
649 670 those revisions. If only one revision is specified then that revision is
650 671 compared to the working directory, and, when no revisions are specified,
651 672 the working directory files are compared to its parent.
652 673
653 674 (use 'hg help -e extdiff' to show help for the extdiff extension)
654 675
655 676 options ([+] can be repeated):
656 677
657 678 -p --program CMD comparison program to run
658 679 -o --option OPT [+] pass option to comparison program
659 680 -r --rev REV [+] revision
660 681 -c --change REV change made by revision
661 682 --patch compare patches for two revisions
662 683 -I --include PATTERN [+] include names matching the given patterns
663 684 -X --exclude PATTERN [+] exclude names matching the given patterns
664 685 -S --subrepos recurse into subrepositories
665 686
666 687 (some details hidden, use --verbose to show complete help)
667 688
668 689
669 690
670 691
671 692
672 693
673 694
674 695
675 696
676 697
677 698 $ hg help --extension extdiff
678 699 extdiff extension - command to allow external programs to compare revisions
679 700
680 701 The extdiff Mercurial extension allows you to use external programs to compare
681 702 revisions, or revision with working directory. The external diff programs are
682 703 called with a configurable set of options and two non-option arguments: paths
683 704 to directories containing snapshots of files to compare.
684 705
685 706 The extdiff extension also allows you to configure new diff commands, so you
686 707 do not need to type 'hg extdiff -p kdiff3' always.
687 708
688 709 [extdiff]
689 710 # add new command that runs GNU diff(1) in 'context diff' mode
690 711 cdiff = gdiff -Nprc5
691 712 ## or the old way:
692 713 #cmd.cdiff = gdiff
693 714 #opts.cdiff = -Nprc5
694 715
695 716 # add new command called meld, runs meld (no need to name twice). If
696 717 # the meld executable is not available, the meld tool in [merge-tools]
697 718 # will be used, if available
698 719 meld =
699 720
700 721 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
701 722 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
702 723 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
703 724 # your .vimrc
704 725 vimdiff = gvim -f "+next" \
705 726 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
706 727
707 728 Tool arguments can include variables that are expanded at runtime:
708 729
709 730 $parent1, $plabel1 - filename, descriptive label of first parent
710 731 $child, $clabel - filename, descriptive label of child revision
711 732 $parent2, $plabel2 - filename, descriptive label of second parent
712 733 $root - repository root
713 734 $parent is an alias for $parent1.
714 735
715 736 The extdiff extension will look in your [diff-tools] and [merge-tools]
716 737 sections for diff tool arguments, when none are specified in [extdiff].
717 738
718 739 [extdiff]
719 740 kdiff3 =
720 741
721 742 [diff-tools]
722 743 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
723 744
724 745 You can use -I/-X and list of file or directory names like normal 'hg diff'
725 746 command. The extdiff extension makes snapshots of only needed files, so
726 747 running the external diff program will actually be pretty fast (at least
727 748 faster than having to compare the entire tree).
728 749
729 750 list of commands:
730 751
731 752 extdiff use external program to diff repository (or selected files)
732 753
733 754 (use 'hg help -v -e extdiff' to show built-in aliases and global options)
734 755
735 756
736 757
737 758
738 759
739 760
740 761
741 762
742 763
743 764
744 765
745 766
746 767
747 768
748 769
749 770
750 771 $ echo 'extdiff = !' >> $HGRCPATH
751 772
752 773 Test help topic with same name as extension
753 774
754 775 $ cat > multirevs.py <<EOF
755 776 > from mercurial import commands, registrar
756 777 > cmdtable = {}
757 778 > command = registrar.command(cmdtable)
758 779 > """multirevs extension
759 780 > Big multi-line module docstring."""
760 781 > @command(b'multirevs', [], 'ARG', norepo=True)
761 782 > def multirevs(ui, repo, arg, *args, **opts):
762 783 > """multirevs command"""
763 784 > pass
764 785 > EOF
765 786 $ echo "multirevs = multirevs.py" >> $HGRCPATH
766 787
767 788 $ hg help multirevs | tail
768 789 bookmark (this works because the last revision of the revset is used):
769 790
770 791 hg update :@
771 792
772 793 - Show diff between tags 1.3 and 1.5 (this works because the first and the
773 794 last revisions of the revset are used):
774 795
775 796 hg diff -r 1.3::1.5
776 797
777 798 use 'hg help -c multirevs' to see help for the multirevs command
778 799
779 800
780 801
781 802
782 803
783 804
784 805 $ hg help -c multirevs
785 806 hg multirevs ARG
786 807
787 808 multirevs command
788 809
789 810 (some details hidden, use --verbose to show complete help)
790 811
791 812
792 813
793 814 $ hg multirevs
794 815 hg multirevs: invalid arguments
795 816 hg multirevs ARG
796 817
797 818 multirevs command
798 819
799 820 (use 'hg multirevs -h' to show more help)
800 821 [255]
801 822
802 823
803 824
804 825 $ echo "multirevs = !" >> $HGRCPATH
805 826
806 827 Issue811: Problem loading extensions twice (by site and by user)
807 828
808 829 $ cat <<EOF >> $HGRCPATH
809 830 > mq =
810 831 > strip =
811 832 > hgext.mq =
812 833 > hgext/mq =
813 834 > EOF
814 835
815 836 Show extensions:
816 837 (note that mq force load strip, also checking it's not loaded twice)
817 838
818 839 $ hg debugextensions
819 840 mq
820 841 strip
821 842
822 843 For extensions, which name matches one of its commands, help
823 844 message should ask '-v -e' to get list of built-in aliases
824 845 along with extension help itself
825 846
826 847 $ mkdir $TESTTMP/d
827 848 $ cat > $TESTTMP/d/dodo.py <<EOF
828 849 > """
829 850 > This is an awesome 'dodo' extension. It does nothing and
830 851 > writes 'Foo foo'
831 852 > """
832 853 > from mercurial import commands, registrar
833 854 > cmdtable = {}
834 855 > command = registrar.command(cmdtable)
835 856 > @command(b'dodo', [], 'hg dodo')
836 857 > def dodo(ui, *args, **kwargs):
837 858 > """Does nothing"""
838 859 > ui.write("I do nothing. Yay\\n")
839 860 > @command(b'foofoo', [], 'hg foofoo')
840 861 > def foofoo(ui, *args, **kwargs):
841 862 > """Writes 'Foo foo'"""
842 863 > ui.write("Foo foo\\n")
843 864 > EOF
844 865 $ dodopath=$TESTTMP/d/dodo.py
845 866
846 867 $ echo "dodo = $dodopath" >> $HGRCPATH
847 868
848 869 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
849 870 $ hg help -e dodo
850 871 dodo extension -
851 872
852 873 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
853 874
854 875 list of commands:
855 876
856 877 dodo Does nothing
857 878 foofoo Writes 'Foo foo'
858 879
859 880 (use 'hg help -v -e dodo' to show built-in aliases and global options)
860 881
861 882 Make sure that '-v -e' prints list of built-in aliases along with
862 883 extension help itself
863 884 $ hg help -v -e dodo
864 885 dodo extension -
865 886
866 887 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
867 888
868 889 list of commands:
869 890
870 891 dodo Does nothing
871 892 foofoo Writes 'Foo foo'
872 893
873 894 global options ([+] can be repeated):
874 895
875 896 -R --repository REPO repository root directory or name of overlay bundle
876 897 file
877 898 --cwd DIR change working directory
878 899 -y --noninteractive do not prompt, automatically pick the first choice for
879 900 all prompts
880 901 -q --quiet suppress output
881 902 -v --verbose enable additional output
882 903 --color TYPE when to colorize (boolean, always, auto, never, or
883 904 debug)
884 905 --config CONFIG [+] set/override config option (use 'section.name=value')
885 906 --debug enable debugging output
886 907 --debugger start debugger
887 908 --encoding ENCODE set the charset encoding (default: ascii)
888 909 --encodingmode MODE set the charset encoding mode (default: strict)
889 910 --traceback always print a traceback on exception
890 911 --time time how long the command takes
891 912 --profile print command execution profile
892 913 --version output version information and exit
893 914 -h --help display help and exit
894 915 --hidden consider hidden changesets
895 916 --pager TYPE when to paginate (boolean, always, auto, or never)
896 917 (default: auto)
897 918
898 919 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
899 920 $ hg help -v dodo
900 921 hg dodo
901 922
902 923 Does nothing
903 924
904 925 (use 'hg help -e dodo' to show help for the dodo extension)
905 926
906 927 options:
907 928
908 929 --mq operate on patch repository
909 930
910 931 global options ([+] can be repeated):
911 932
912 933 -R --repository REPO repository root directory or name of overlay bundle
913 934 file
914 935 --cwd DIR change working directory
915 936 -y --noninteractive do not prompt, automatically pick the first choice for
916 937 all prompts
917 938 -q --quiet suppress output
918 939 -v --verbose enable additional output
919 940 --color TYPE when to colorize (boolean, always, auto, never, or
920 941 debug)
921 942 --config CONFIG [+] set/override config option (use 'section.name=value')
922 943 --debug enable debugging output
923 944 --debugger start debugger
924 945 --encoding ENCODE set the charset encoding (default: ascii)
925 946 --encodingmode MODE set the charset encoding mode (default: strict)
926 947 --traceback always print a traceback on exception
927 948 --time time how long the command takes
928 949 --profile print command execution profile
929 950 --version output version information and exit
930 951 -h --help display help and exit
931 952 --hidden consider hidden changesets
932 953 --pager TYPE when to paginate (boolean, always, auto, or never)
933 954 (default: auto)
934 955
935 956 In case when extension name doesn't match any of its commands,
936 957 help message should ask for '-v' to get list of built-in aliases
937 958 along with extension help
938 959 $ cat > $TESTTMP/d/dudu.py <<EOF
939 960 > """
940 961 > This is an awesome 'dudu' extension. It does something and
941 962 > also writes 'Beep beep'
942 963 > """
943 964 > from mercurial import commands, registrar
944 965 > cmdtable = {}
945 966 > command = registrar.command(cmdtable)
946 967 > @command(b'something', [], 'hg something')
947 968 > def something(ui, *args, **kwargs):
948 969 > """Does something"""
949 970 > ui.write("I do something. Yaaay\\n")
950 971 > @command(b'beep', [], 'hg beep')
951 972 > def beep(ui, *args, **kwargs):
952 973 > """Writes 'Beep beep'"""
953 974 > ui.write("Beep beep\\n")
954 975 > EOF
955 976 $ dudupath=$TESTTMP/d/dudu.py
956 977
957 978 $ echo "dudu = $dudupath" >> $HGRCPATH
958 979
959 980 $ hg help -e dudu
960 981 dudu extension -
961 982
962 983 This is an awesome 'dudu' extension. It does something and also writes 'Beep
963 984 beep'
964 985
965 986 list of commands:
966 987
967 988 beep Writes 'Beep beep'
968 989 something Does something
969 990
970 991 (use 'hg help -v dudu' to show built-in aliases and global options)
971 992
972 993 In case when extension name doesn't match any of its commands,
973 994 help options '-v' and '-v -e' should be equivalent
974 995 $ hg help -v dudu
975 996 dudu extension -
976 997
977 998 This is an awesome 'dudu' extension. It does something and also writes 'Beep
978 999 beep'
979 1000
980 1001 list of commands:
981 1002
982 1003 beep Writes 'Beep beep'
983 1004 something Does something
984 1005
985 1006 global options ([+] can be repeated):
986 1007
987 1008 -R --repository REPO repository root directory or name of overlay bundle
988 1009 file
989 1010 --cwd DIR change working directory
990 1011 -y --noninteractive do not prompt, automatically pick the first choice for
991 1012 all prompts
992 1013 -q --quiet suppress output
993 1014 -v --verbose enable additional output
994 1015 --color TYPE when to colorize (boolean, always, auto, never, or
995 1016 debug)
996 1017 --config CONFIG [+] set/override config option (use 'section.name=value')
997 1018 --debug enable debugging output
998 1019 --debugger start debugger
999 1020 --encoding ENCODE set the charset encoding (default: ascii)
1000 1021 --encodingmode MODE set the charset encoding mode (default: strict)
1001 1022 --traceback always print a traceback on exception
1002 1023 --time time how long the command takes
1003 1024 --profile print command execution profile
1004 1025 --version output version information and exit
1005 1026 -h --help display help and exit
1006 1027 --hidden consider hidden changesets
1007 1028 --pager TYPE when to paginate (boolean, always, auto, or never)
1008 1029 (default: auto)
1009 1030
1010 1031 $ hg help -v -e dudu
1011 1032 dudu extension -
1012 1033
1013 1034 This is an awesome 'dudu' extension. It does something and also writes 'Beep
1014 1035 beep'
1015 1036
1016 1037 list of commands:
1017 1038
1018 1039 beep Writes 'Beep beep'
1019 1040 something Does something
1020 1041
1021 1042 global options ([+] can be repeated):
1022 1043
1023 1044 -R --repository REPO repository root directory or name of overlay bundle
1024 1045 file
1025 1046 --cwd DIR change working directory
1026 1047 -y --noninteractive do not prompt, automatically pick the first choice for
1027 1048 all prompts
1028 1049 -q --quiet suppress output
1029 1050 -v --verbose enable additional output
1030 1051 --color TYPE when to colorize (boolean, always, auto, never, or
1031 1052 debug)
1032 1053 --config CONFIG [+] set/override config option (use 'section.name=value')
1033 1054 --debug enable debugging output
1034 1055 --debugger start debugger
1035 1056 --encoding ENCODE set the charset encoding (default: ascii)
1036 1057 --encodingmode MODE set the charset encoding mode (default: strict)
1037 1058 --traceback always print a traceback on exception
1038 1059 --time time how long the command takes
1039 1060 --profile print command execution profile
1040 1061 --version output version information and exit
1041 1062 -h --help display help and exit
1042 1063 --hidden consider hidden changesets
1043 1064 --pager TYPE when to paginate (boolean, always, auto, or never)
1044 1065 (default: auto)
1045 1066
1046 1067 Disabled extension commands:
1047 1068
1048 1069 $ ORGHGRCPATH=$HGRCPATH
1049 1070 $ HGRCPATH=
1050 1071 $ export HGRCPATH
1051 1072 $ hg help email
1052 1073 'email' is provided by the following extension:
1053 1074
1054 1075 patchbomb command to send changesets as (a series of) patch emails
1055 1076
1056 1077 (use 'hg help extensions' for information on enabling extensions)
1057 1078
1058 1079
1059 1080 $ hg qdel
1060 1081 hg: unknown command 'qdel'
1061 1082 'qdelete' is provided by the following extension:
1062 1083
1063 1084 mq manage a stack of patches
1064 1085
1065 1086 (use 'hg help extensions' for information on enabling extensions)
1066 1087 [255]
1067 1088
1068 1089
1069 1090 $ hg churn
1070 1091 hg: unknown command 'churn'
1071 1092 'churn' is provided by the following extension:
1072 1093
1073 1094 churn command to display statistics about repository history
1074 1095
1075 1096 (use 'hg help extensions' for information on enabling extensions)
1076 1097 [255]
1077 1098
1078 1099
1079 1100
1080 1101 Disabled extensions:
1081 1102
1082 1103 $ hg help churn
1083 1104 churn extension - command to display statistics about repository history
1084 1105
1085 1106 (use 'hg help extensions' for information on enabling extensions)
1086 1107
1087 1108 $ hg help patchbomb
1088 1109 patchbomb extension - command to send changesets as (a series of) patch emails
1089 1110
1090 1111 The series is started off with a "[PATCH 0 of N]" introduction, which
1091 1112 describes the series as a whole.
1092 1113
1093 1114 Each patch email has a Subject line of "[PATCH M of N] ...", using the first
1094 1115 line of the changeset description as the subject text. The message contains
1095 1116 two or three body parts:
1096 1117
1097 1118 - The changeset description.
1098 1119 - [Optional] The result of running diffstat on the patch.
1099 1120 - The patch itself, as generated by 'hg export'.
1100 1121
1101 1122 Each message refers to the first in the series using the In-Reply-To and
1102 1123 References headers, so they will show up as a sequence in threaded mail and
1103 1124 news readers, and in mail archives.
1104 1125
1105 1126 To configure other defaults, add a section like this to your configuration
1106 1127 file:
1107 1128
1108 1129 [email]
1109 1130 from = My Name <my@email>
1110 1131 to = recipient1, recipient2, ...
1111 1132 cc = cc1, cc2, ...
1112 1133 bcc = bcc1, bcc2, ...
1113 1134 reply-to = address1, address2, ...
1114 1135
1115 1136 Use "[patchbomb]" as configuration section name if you need to override global
1116 1137 "[email]" address settings.
1117 1138
1118 1139 Then you can use the 'hg email' command to mail a series of changesets as a
1119 1140 patchbomb.
1120 1141
1121 1142 You can also either configure the method option in the email section to be a
1122 1143 sendmail compatible mailer or fill out the [smtp] section so that the
1123 1144 patchbomb extension can automatically send patchbombs directly from the
1124 1145 commandline. See the [email] and [smtp] sections in hgrc(5) for details.
1125 1146
1126 1147 By default, 'hg email' will prompt for a "To" or "CC" header if you do not
1127 1148 supply one via configuration or the command line. You can override this to
1128 1149 never prompt by configuring an empty value:
1129 1150
1130 1151 [email]
1131 1152 cc =
1132 1153
1133 1154 You can control the default inclusion of an introduction message with the
1134 1155 "patchbomb.intro" configuration option. The configuration is always
1135 1156 overwritten by command line flags like --intro and --desc:
1136 1157
1137 1158 [patchbomb]
1138 1159 intro=auto # include introduction message if more than 1 patch (default)
1139 1160 intro=never # never include an introduction message
1140 1161 intro=always # always include an introduction message
1141 1162
1142 1163 You can specify a template for flags to be added in subject prefixes. Flags
1143 1164 specified by --flag option are exported as "{flags}" keyword:
1144 1165
1145 1166 [patchbomb]
1146 1167 flagtemplate = "{separate(' ',
1147 1168 ifeq(branch, 'default', '', branch|upper),
1148 1169 flags)}"
1149 1170
1150 1171 You can set patchbomb to always ask for confirmation by setting
1151 1172 "patchbomb.confirm" to true.
1152 1173
1153 1174 (use 'hg help extensions' for information on enabling extensions)
1154 1175
1155 1176
1156 1177 Broken disabled extension and command:
1157 1178
1158 1179 $ mkdir hgext
1159 1180 $ echo > hgext/__init__.py
1160 1181 $ cat > hgext/broken.py <<EOF
1161 1182 > "broken extension'
1162 1183 > EOF
1163 1184 $ cat > path.py <<EOF
1164 1185 > import os, sys
1165 1186 > sys.path.insert(0, os.environ['HGEXTPATH'])
1166 1187 > EOF
1167 1188 $ HGEXTPATH=`pwd`
1168 1189 $ export HGEXTPATH
1169 1190
1170 1191 $ hg --config extensions.path=./path.py help broken
1171 1192 broken extension - (no help text available)
1172 1193
1173 1194 (use 'hg help extensions' for information on enabling extensions)
1174 1195
1175 1196
1176 1197 $ cat > hgext/forest.py <<EOF
1177 1198 > cmdtable = None
1178 1199 > EOF
1179 1200 $ hg --config extensions.path=./path.py help foo > /dev/null
1180 1201 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
1181 1202 abort: no such help topic: foo
1182 1203 (try 'hg help --keyword foo')
1183 1204 [255]
1184 1205
1185 1206 $ cat > throw.py <<EOF
1186 1207 > from mercurial import commands, registrar, util
1187 1208 > cmdtable = {}
1188 1209 > command = registrar.command(cmdtable)
1189 1210 > class Bogon(Exception): pass
1190 1211 > @command(b'throw', [], 'hg throw', norepo=True)
1191 1212 > def throw(ui, **opts):
1192 1213 > """throws an exception"""
1193 1214 > raise Bogon()
1194 1215 > EOF
1195 1216
1196 1217 No declared supported version, extension complains:
1197 1218 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1198 1219 ** Unknown exception encountered with possibly-broken third-party extension throw
1199 1220 ** which supports versions unknown of Mercurial.
1200 1221 ** Please disable throw and try your action again.
1201 1222 ** If that fixes the bug please report it to the extension author.
1202 1223 ** Python * (glob)
1203 1224 ** Mercurial Distributed SCM * (glob)
1204 1225 ** Extensions loaded: throw
1205 1226
1206 1227 empty declaration of supported version, extension complains:
1207 1228 $ echo "testedwith = ''" >> throw.py
1208 1229 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1209 1230 ** Unknown exception encountered with possibly-broken third-party extension throw
1210 1231 ** which supports versions unknown of Mercurial.
1211 1232 ** Please disable throw and try your action again.
1212 1233 ** If that fixes the bug please report it to the extension author.
1213 1234 ** Python * (glob)
1214 1235 ** Mercurial Distributed SCM (*) (glob)
1215 1236 ** Extensions loaded: throw
1216 1237
1217 1238 If the extension specifies a buglink, show that:
1218 1239 $ echo 'buglink = "http://example.com/bts"' >> throw.py
1219 1240 $ rm -f throw.pyc throw.pyo
1220 1241 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1221 1242 ** Unknown exception encountered with possibly-broken third-party extension throw
1222 1243 ** which supports versions unknown of Mercurial.
1223 1244 ** Please disable throw and try your action again.
1224 1245 ** If that fixes the bug please report it to http://example.com/bts
1225 1246 ** Python * (glob)
1226 1247 ** Mercurial Distributed SCM (*) (glob)
1227 1248 ** Extensions loaded: throw
1228 1249
1229 1250 If the extensions declare outdated versions, accuse the older extension first:
1230 1251 $ echo "from mercurial import util" >> older.py
1231 1252 $ echo "util.version = lambda:'2.2'" >> older.py
1232 1253 $ echo "testedwith = '1.9.3'" >> older.py
1233 1254 $ echo "testedwith = '2.1.1'" >> throw.py
1234 1255 $ rm -f throw.pyc throw.pyo
1235 1256 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1236 1257 > throw 2>&1 | egrep '^\*\*'
1237 1258 ** Unknown exception encountered with possibly-broken third-party extension older
1238 1259 ** which supports versions 1.9 of Mercurial.
1239 1260 ** Please disable older and try your action again.
1240 1261 ** If that fixes the bug please report it to the extension author.
1241 1262 ** Python * (glob)
1242 1263 ** Mercurial Distributed SCM (version 2.2)
1243 1264 ** Extensions loaded: throw, older
1244 1265
1245 1266 One extension only tested with older, one only with newer versions:
1246 1267 $ echo "util.version = lambda:'2.1'" >> older.py
1247 1268 $ rm -f older.pyc older.pyo
1248 1269 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1249 1270 > throw 2>&1 | egrep '^\*\*'
1250 1271 ** Unknown exception encountered with possibly-broken third-party extension older
1251 1272 ** which supports versions 1.9 of Mercurial.
1252 1273 ** Please disable older and try your action again.
1253 1274 ** If that fixes the bug please report it to the extension author.
1254 1275 ** Python * (glob)
1255 1276 ** Mercurial Distributed SCM (version 2.1)
1256 1277 ** Extensions loaded: throw, older
1257 1278
1258 1279 Older extension is tested with current version, the other only with newer:
1259 1280 $ echo "util.version = lambda:'1.9.3'" >> older.py
1260 1281 $ rm -f older.pyc older.pyo
1261 1282 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1262 1283 > throw 2>&1 | egrep '^\*\*'
1263 1284 ** Unknown exception encountered with possibly-broken third-party extension throw
1264 1285 ** which supports versions 2.1 of Mercurial.
1265 1286 ** Please disable throw and try your action again.
1266 1287 ** If that fixes the bug please report it to http://example.com/bts
1267 1288 ** Python * (glob)
1268 1289 ** Mercurial Distributed SCM (version 1.9.3)
1269 1290 ** Extensions loaded: throw, older
1270 1291
1271 1292 Ability to point to a different point
1272 1293 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
1273 1294 > --config ui.supportcontact='Your Local Goat Lenders' throw 2>&1 | egrep '^\*\*'
1274 1295 ** unknown exception encountered, please report by visiting
1275 1296 ** Your Local Goat Lenders
1276 1297 ** Python * (glob)
1277 1298 ** Mercurial Distributed SCM (*) (glob)
1278 1299 ** Extensions loaded: throw, older
1279 1300
1280 1301 Declare the version as supporting this hg version, show regular bts link:
1281 1302 $ hgver=`hg debuginstall -T '{hgver}'`
1282 1303 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
1283 1304 $ if [ -z "$hgver" ]; then
1284 1305 > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
1285 1306 > fi
1286 1307 $ rm -f throw.pyc throw.pyo
1287 1308 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1288 1309 ** unknown exception encountered, please report by visiting
1289 1310 ** https://mercurial-scm.org/wiki/BugTracker
1290 1311 ** Python * (glob)
1291 1312 ** Mercurial Distributed SCM (*) (glob)
1292 1313 ** Extensions loaded: throw
1293 1314
1294 1315 Patch version is ignored during compatibility check
1295 1316 $ echo "testedwith = '3.2'" >> throw.py
1296 1317 $ echo "util.version = lambda:'3.2.2'" >> throw.py
1297 1318 $ rm -f throw.pyc throw.pyo
1298 1319 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
1299 1320 ** unknown exception encountered, please report by visiting
1300 1321 ** https://mercurial-scm.org/wiki/BugTracker
1301 1322 ** Python * (glob)
1302 1323 ** Mercurial Distributed SCM (*) (glob)
1303 1324 ** Extensions loaded: throw
1304 1325
1305 1326 Test version number support in 'hg version':
1306 1327 $ echo '__version__ = (1, 2, 3)' >> throw.py
1307 1328 $ rm -f throw.pyc throw.pyo
1308 1329 $ hg version -v
1309 1330 Mercurial Distributed SCM (version *) (glob)
1310 1331 (see https://mercurial-scm.org for more information)
1311 1332
1312 1333 Copyright (C) 2005-* Matt Mackall and others (glob)
1313 1334 This is free software; see the source for copying conditions. There is NO
1314 1335 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1315 1336
1316 1337 Enabled extensions:
1317 1338
1318 1339
1319 1340 $ hg version -v --config extensions.throw=throw.py
1320 1341 Mercurial Distributed SCM (version *) (glob)
1321 1342 (see https://mercurial-scm.org for more information)
1322 1343
1323 1344 Copyright (C) 2005-* Matt Mackall and others (glob)
1324 1345 This is free software; see the source for copying conditions. There is NO
1325 1346 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1326 1347
1327 1348 Enabled extensions:
1328 1349
1329 1350 throw external 1.2.3
1330 1351 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
1331 1352 $ rm -f throw.pyc throw.pyo
1332 1353 $ hg version -v --config extensions.throw=throw.py --config extensions.strip=
1333 1354 Mercurial Distributed SCM (version *) (glob)
1334 1355 (see https://mercurial-scm.org for more information)
1335 1356
1336 1357 Copyright (C) 2005-* Matt Mackall and others (glob)
1337 1358 This is free software; see the source for copying conditions. There is NO
1338 1359 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1339 1360
1340 1361 Enabled extensions:
1341 1362
1342 1363 throw external 1.twentythree
1343 1364 strip internal
1344 1365
1345 1366 $ hg version -q --config extensions.throw=throw.py
1346 1367 Mercurial Distributed SCM (version *) (glob)
1347 1368
1348 1369 Test JSON output of version:
1349 1370
1350 1371 $ hg version -Tjson
1351 1372 [
1352 1373 {
1353 1374 "extensions": [],
1354 1375 "ver": "*" (glob)
1355 1376 }
1356 1377 ]
1357 1378
1358 1379 $ hg version --config extensions.throw=throw.py -Tjson
1359 1380 [
1360 1381 {
1361 1382 "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}],
1362 1383 "ver": "3.2.2"
1363 1384 }
1364 1385 ]
1365 1386
1366 1387 $ hg version --config extensions.strip= -Tjson
1367 1388 [
1368 1389 {
1369 1390 "extensions": [{"bundled": true, "name": "strip", "ver": null}],
1370 1391 "ver": "*" (glob)
1371 1392 }
1372 1393 ]
1373 1394
1374 1395 Test template output of version:
1375 1396
1376 1397 $ hg version --config extensions.throw=throw.py --config extensions.strip= \
1377 1398 > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}'
1378 1399 throw 1.twentythree (external)
1379 1400 strip (internal)
1380 1401
1381 1402 Refuse to load extensions with minimum version requirements
1382 1403
1383 1404 $ cat > minversion1.py << EOF
1384 1405 > from mercurial import util
1385 1406 > util.version = lambda: '3.5.2'
1386 1407 > minimumhgversion = '3.6'
1387 1408 > EOF
1388 1409 $ hg --config extensions.minversion=minversion1.py version
1389 1410 (third party extension minversion requires version 3.6 or newer of Mercurial; disabling)
1390 1411 Mercurial Distributed SCM (version 3.5.2)
1391 1412 (see https://mercurial-scm.org for more information)
1392 1413
1393 1414 Copyright (C) 2005-* Matt Mackall and others (glob)
1394 1415 This is free software; see the source for copying conditions. There is NO
1395 1416 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1396 1417
1397 1418 $ cat > minversion2.py << EOF
1398 1419 > from mercurial import util
1399 1420 > util.version = lambda: '3.6'
1400 1421 > minimumhgversion = '3.7'
1401 1422 > EOF
1402 1423 $ hg --config extensions.minversion=minversion2.py version 2>&1 | egrep '\(third'
1403 1424 (third party extension minversion requires version 3.7 or newer of Mercurial; disabling)
1404 1425
1405 1426 Can load version that is only off by point release
1406 1427
1407 1428 $ cat > minversion2.py << EOF
1408 1429 > from mercurial import util
1409 1430 > util.version = lambda: '3.6.1'
1410 1431 > minimumhgversion = '3.6'
1411 1432 > EOF
1412 1433 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1413 1434 [1]
1414 1435
1415 1436 Can load minimum version identical to current
1416 1437
1417 1438 $ cat > minversion3.py << EOF
1418 1439 > from mercurial import util
1419 1440 > util.version = lambda: '3.5'
1420 1441 > minimumhgversion = '3.5'
1421 1442 > EOF
1422 1443 $ hg --config extensions.minversion=minversion3.py version 2>&1 | egrep '\(third'
1423 1444 [1]
1424 1445
1425 1446 Restore HGRCPATH
1426 1447
1427 1448 $ HGRCPATH=$ORGHGRCPATH
1428 1449 $ export HGRCPATH
1429 1450
1430 1451 Commands handling multiple repositories at a time should invoke only
1431 1452 "reposetup()" of extensions enabling in the target repository.
1432 1453
1433 1454 $ mkdir reposetup-test
1434 1455 $ cd reposetup-test
1435 1456
1436 1457 $ cat > $TESTTMP/reposetuptest.py <<EOF
1437 1458 > from mercurial import extensions
1438 1459 > def reposetup(ui, repo):
1439 1460 > ui.write('reposetup() for %s\n' % (repo.root))
1440 1461 > ui.flush()
1441 1462 > EOF
1442 1463 $ hg init src
1443 1464 $ echo a > src/a
1444 1465 $ hg -R src commit -Am '#0 at src/a'
1445 1466 adding a
1446 1467 $ echo '[extensions]' >> src/.hg/hgrc
1447 1468 $ echo '# enable extension locally' >> src/.hg/hgrc
1448 1469 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1449 1470 $ hg -R src status
1450 1471 reposetup() for $TESTTMP/reposetup-test/src (glob)
1451 1472
1452 1473 $ hg clone -U src clone-dst1
1453 1474 reposetup() for $TESTTMP/reposetup-test/src (glob)
1454 1475 $ hg init push-dst1
1455 1476 $ hg -q -R src push push-dst1
1456 1477 reposetup() for $TESTTMP/reposetup-test/src (glob)
1457 1478 $ hg init pull-src1
1458 1479 $ hg -q -R pull-src1 pull src
1459 1480 reposetup() for $TESTTMP/reposetup-test/src (glob)
1460 1481
1461 1482 $ cat <<EOF >> $HGRCPATH
1462 1483 > [extensions]
1463 1484 > # disable extension globally and explicitly
1464 1485 > reposetuptest = !
1465 1486 > EOF
1466 1487 $ hg clone -U src clone-dst2
1467 1488 reposetup() for $TESTTMP/reposetup-test/src (glob)
1468 1489 $ hg init push-dst2
1469 1490 $ hg -q -R src push push-dst2
1470 1491 reposetup() for $TESTTMP/reposetup-test/src (glob)
1471 1492 $ hg init pull-src2
1472 1493 $ hg -q -R pull-src2 pull src
1473 1494 reposetup() for $TESTTMP/reposetup-test/src (glob)
1474 1495
1475 1496 $ cat <<EOF >> $HGRCPATH
1476 1497 > [extensions]
1477 1498 > # enable extension globally
1478 1499 > reposetuptest = $TESTTMP/reposetuptest.py
1479 1500 > EOF
1480 1501 $ hg clone -U src clone-dst3
1481 1502 reposetup() for $TESTTMP/reposetup-test/src (glob)
1482 1503 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1483 1504 $ hg init push-dst3
1484 1505 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1485 1506 $ hg -q -R src push push-dst3
1486 1507 reposetup() for $TESTTMP/reposetup-test/src (glob)
1487 1508 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1488 1509 $ hg init pull-src3
1489 1510 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1490 1511 $ hg -q -R pull-src3 pull src
1491 1512 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1492 1513 reposetup() for $TESTTMP/reposetup-test/src (glob)
1493 1514
1494 1515 $ echo '[extensions]' >> src/.hg/hgrc
1495 1516 $ echo '# disable extension locally' >> src/.hg/hgrc
1496 1517 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1497 1518 $ hg clone -U src clone-dst4
1498 1519 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1499 1520 $ hg init push-dst4
1500 1521 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1501 1522 $ hg -q -R src push push-dst4
1502 1523 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1503 1524 $ hg init pull-src4
1504 1525 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1505 1526 $ hg -q -R pull-src4 pull src
1506 1527 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1507 1528
1508 1529 disabling in command line overlays with all configuration
1509 1530 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1510 1531 $ hg --config extensions.reposetuptest=! init push-dst5
1511 1532 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1512 1533 $ hg --config extensions.reposetuptest=! init pull-src5
1513 1534 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1514 1535
1515 1536 $ cat <<EOF >> $HGRCPATH
1516 1537 > [extensions]
1517 1538 > # disable extension globally and explicitly
1518 1539 > reposetuptest = !
1519 1540 > EOF
1520 1541 $ hg init parent
1521 1542 $ hg init parent/sub1
1522 1543 $ echo 1 > parent/sub1/1
1523 1544 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1524 1545 adding 1
1525 1546 $ hg init parent/sub2
1526 1547 $ hg init parent/sub2/sub21
1527 1548 $ echo 21 > parent/sub2/sub21/21
1528 1549 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1529 1550 adding 21
1530 1551 $ cat > parent/sub2/.hgsub <<EOF
1531 1552 > sub21 = sub21
1532 1553 > EOF
1533 1554 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1534 1555 adding .hgsub
1535 1556 $ hg init parent/sub3
1536 1557 $ echo 3 > parent/sub3/3
1537 1558 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1538 1559 adding 3
1539 1560 $ cat > parent/.hgsub <<EOF
1540 1561 > sub1 = sub1
1541 1562 > sub2 = sub2
1542 1563 > sub3 = sub3
1543 1564 > EOF
1544 1565 $ hg -R parent commit -Am '#0 at parent'
1545 1566 adding .hgsub
1546 1567 $ echo '[extensions]' >> parent/.hg/hgrc
1547 1568 $ echo '# enable extension locally' >> parent/.hg/hgrc
1548 1569 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1549 1570 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1550 1571 $ hg -R parent status -S -A
1551 1572 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1552 1573 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1553 1574 C .hgsub
1554 1575 C .hgsubstate
1555 1576 C sub1/1
1556 1577 C sub2/.hgsub
1557 1578 C sub2/.hgsubstate
1558 1579 C sub2/sub21/21
1559 1580 C sub3/3
1560 1581
1561 1582 $ cd ..
1562 1583
1563 1584 Prohibit registration of commands that don't use @command (issue5137)
1564 1585
1565 1586 $ hg init deprecated
1566 1587 $ cd deprecated
1567 1588
1568 1589 $ cat <<EOF > deprecatedcmd.py
1569 1590 > def deprecatedcmd(repo, ui):
1570 1591 > pass
1571 1592 > cmdtable = {
1572 1593 > 'deprecatedcmd': (deprecatedcmd, [], ''),
1573 1594 > }
1574 1595 > EOF
1575 1596 $ cat <<EOF > .hg/hgrc
1576 1597 > [extensions]
1577 1598 > deprecatedcmd = `pwd`/deprecatedcmd.py
1578 1599 > mq = !
1579 1600 > hgext.mq = !
1580 1601 > hgext/mq = !
1581 1602 > EOF
1582 1603
1583 1604 $ hg deprecatedcmd > /dev/null
1584 1605 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1585 1606 *** (use @command decorator to register 'deprecatedcmd')
1586 1607 hg: unknown command 'deprecatedcmd'
1587 1608 [255]
1588 1609
1589 1610 the extension shouldn't be loaded at all so the mq works:
1590 1611
1591 1612 $ hg qseries --config extensions.mq= > /dev/null
1592 1613 *** failed to import extension deprecatedcmd from $TESTTMP/deprecated/deprecatedcmd.py: missing attributes: norepo, optionalrepo, inferrepo
1593 1614 *** (use @command decorator to register 'deprecatedcmd')
1594 1615
1595 1616 $ cd ..
1596 1617
1597 1618 Test synopsis and docstring extending
1598 1619
1599 1620 $ hg init exthelp
1600 1621 $ cat > exthelp.py <<EOF
1601 1622 > from mercurial import commands, extensions
1602 1623 > def exbookmarks(orig, *args, **opts):
1603 1624 > return orig(*args, **opts)
1604 1625 > def uisetup(ui):
1605 1626 > synopsis = ' GREPME [--foo] [-x]'
1606 1627 > docstring = '''
1607 1628 > GREPME make sure that this is in the help!
1608 1629 > '''
1609 1630 > extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
1610 1631 > synopsis, docstring)
1611 1632 > EOF
1612 1633 $ abspath=`pwd`/exthelp.py
1613 1634 $ echo '[extensions]' >> $HGRCPATH
1614 1635 $ echo "exthelp = $abspath" >> $HGRCPATH
1615 1636 $ cd exthelp
1616 1637 $ hg help bookmarks | grep GREPME
1617 1638 hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
1618 1639 GREPME make sure that this is in the help!
1619 1640 $ cd ..
1620 1641
1621 1642 Show deprecation warning for the use of cmdutil.command
1622 1643
1623 1644 $ cat > nonregistrar.py <<EOF
1624 1645 > from mercurial import cmdutil
1625 1646 > cmdtable = {}
1626 1647 > command = cmdutil.command(cmdtable)
1627 1648 > @command(b'foo', [], norepo=True)
1628 1649 > def foo(ui):
1629 1650 > pass
1630 1651 > EOF
1631 1652
1632 1653 $ hg --config extensions.nonregistrar=`pwd`/nonregistrar.py version > /dev/null
1633 1654 devel-warn: cmdutil.command is deprecated, use registrar.command to register 'foo'
1634 1655 (compatibility will be dropped after Mercurial-4.6, update your code.) * (glob)
1635 1656
1636 1657 Make sure a broken uisetup doesn't globally break hg:
1637 1658 $ cat > $TESTTMP/baduisetup.py <<EOF
1638 1659 > from mercurial import (
1639 1660 > bdiff,
1640 1661 > extensions,
1641 1662 > )
1642 1663 >
1643 1664 > def blockswrapper(orig, *args, **kwargs):
1644 1665 > return orig(*args, **kwargs)
1645 1666 >
1646 1667 > def uisetup(ui):
1647 1668 > extensions.wrapfunction(bdiff, 'blocks', blockswrapper)
1648 1669 > EOF
1649 1670 $ cat >> $HGRCPATH <<EOF
1650 1671 > [extensions]
1651 1672 > baduisetup = $TESTTMP/baduisetup.py
1652 1673 > EOF
1653 1674
1654 1675 Even though the extension fails during uisetup, hg is still basically usable:
1655 1676 $ hg version
1656 1677 *** failed to set up extension baduisetup: No module named bdiff
1657 1678 Mercurial Distributed SCM (version *) (glob)
1658 1679 (see https://mercurial-scm.org for more information)
1659 1680
1660 1681 Copyright (C) 2005-2017 Matt Mackall and others
1661 1682 This is free software; see the source for copying conditions. There is NO
1662 1683 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1663 1684
1664 1685 $ hg version --traceback
1665 1686 Traceback (most recent call last):
1666 1687 File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
1667 1688 uisetup(ui)
1668 1689 File "$TESTTMP/baduisetup.py", line 10, in uisetup
1669 1690 extensions.wrapfunction(bdiff, 'blocks', blockswrapper)
1670 1691 File "*/mercurial/extensions.py", line *, in wrapfunction (glob)
1671 1692 origfn = getattr(container, funcname)
1672 1693 File "*/hgdemandimport/demandimportpy2.py", line *, in __getattr__ (glob)
1673 1694 self._load()
1674 1695 File "*/hgdemandimport/demandimportpy2.py", line *, in _load (glob)
1675 1696 mod = _hgextimport(_origimport, head, globals, locals, None, level)
1676 1697 File "*/hgdemandimport/demandimportpy2.py", line *, in _hgextimport (glob)
1677 1698 return importfunc(name, globals, *args, **kwargs)
1678 1699 ImportError: No module named bdiff
1679 1700 *** failed to set up extension baduisetup: No module named bdiff
1680 1701 Mercurial Distributed SCM (version *) (glob)
1681 1702 (see https://mercurial-scm.org for more information)
1682 1703
1683 1704 Copyright (C) 2005-2017 Matt Mackall and others
1684 1705 This is free software; see the source for copying conditions. There is NO
1685 1706 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
General Comments 0
You need to be logged in to leave comments. Login now