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