##// END OF EJS Templates
demandimport: allow extensions to import own modules by absolute name...
FUJIWARA Katsunori -
r19933:621a26eb default
parent child Browse files
Show More
@@ -1,156 +1,171
1 # demandimport.py - global demand-loading of modules for Mercurial
1 # demandimport.py - global demand-loading of modules for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''
8 '''
9 demandimport - automatic demandloading of modules
9 demandimport - automatic demandloading of modules
10
10
11 To enable this module, do:
11 To enable this module, do:
12
12
13 import demandimport; demandimport.enable()
13 import demandimport; demandimport.enable()
14
14
15 Imports of the following forms will be demand-loaded:
15 Imports of the following forms will be demand-loaded:
16
16
17 import a, b.c
17 import a, b.c
18 import a.b as c
18 import a.b as c
19 from a import b,c # a will be loaded immediately
19 from a import b,c # a will be loaded immediately
20
20
21 These imports will not be delayed:
21 These imports will not be delayed:
22
22
23 from a import *
23 from a import *
24 b = __import__(a)
24 b = __import__(a)
25 '''
25 '''
26
26
27 import __builtin__
27 import __builtin__
28 _origimport = __import__
28 _origimport = __import__
29
29
30 nothing = object()
30 nothing = object()
31
31
32 try:
32 try:
33 _origimport(__builtin__.__name__, {}, {}, None, -1)
33 _origimport(__builtin__.__name__, {}, {}, None, -1)
34 except TypeError: # no level argument
34 except TypeError: # no level argument
35 def _import(name, globals, locals, fromlist, level):
35 def _import(name, globals, locals, fromlist, level):
36 "call _origimport with no level argument"
36 "call _origimport with no level argument"
37 return _origimport(name, globals, locals, fromlist)
37 return _origimport(name, globals, locals, fromlist)
38 else:
38 else:
39 _import = _origimport
39 _import = _origimport
40
40
41 def _hgextimport(importfunc, name, globals, *args):
42 try:
43 return importfunc(name, globals, *args)
44 except ImportError:
45 if not globals:
46 raise
47 # extensions are loaded with "hgext_" prefix
48 hgextname = 'hgext_%s' % name
49 nameroot = hgextname.split('.', 1)[0]
50 contextroot = globals.get('__name__', '').split('.', 1)[0]
51 if nameroot != contextroot:
52 raise
53 # retry to import with "hgext_" prefix
54 return importfunc(hgextname, globals, *args)
55
41 class _demandmod(object):
56 class _demandmod(object):
42 """module demand-loader and proxy"""
57 """module demand-loader and proxy"""
43 def __init__(self, name, globals, locals, level=-1):
58 def __init__(self, name, globals, locals, level=-1):
44 if '.' in name:
59 if '.' in name:
45 head, rest = name.split('.', 1)
60 head, rest = name.split('.', 1)
46 after = [rest]
61 after = [rest]
47 else:
62 else:
48 head = name
63 head = name
49 after = []
64 after = []
50 object.__setattr__(self, "_data",
65 object.__setattr__(self, "_data",
51 (head, globals, locals, after, level))
66 (head, globals, locals, after, level))
52 object.__setattr__(self, "_module", None)
67 object.__setattr__(self, "_module", None)
53 def _extend(self, name):
68 def _extend(self, name):
54 """add to the list of submodules to load"""
69 """add to the list of submodules to load"""
55 self._data[3].append(name)
70 self._data[3].append(name)
56 def _load(self):
71 def _load(self):
57 if not self._module:
72 if not self._module:
58 head, globals, locals, after, level = self._data
73 head, globals, locals, after, level = self._data
59 mod = _import(head, globals, locals, None, level)
74 mod = _hgextimport(_import, head, globals, locals, None, level)
60 # load submodules
75 # load submodules
61 def subload(mod, p):
76 def subload(mod, p):
62 h, t = p, None
77 h, t = p, None
63 if '.' in p:
78 if '.' in p:
64 h, t = p.split('.', 1)
79 h, t = p.split('.', 1)
65 if getattr(mod, h, nothing) is nothing:
80 if getattr(mod, h, nothing) is nothing:
66 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
81 setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
67 elif t:
82 elif t:
68 subload(getattr(mod, h), t)
83 subload(getattr(mod, h), t)
69
84
70 for x in after:
85 for x in after:
71 subload(mod, x)
86 subload(mod, x)
72
87
73 # are we in the locals dictionary still?
88 # are we in the locals dictionary still?
74 if locals and locals.get(head) == self:
89 if locals and locals.get(head) == self:
75 locals[head] = mod
90 locals[head] = mod
76 object.__setattr__(self, "_module", mod)
91 object.__setattr__(self, "_module", mod)
77
92
78 def __repr__(self):
93 def __repr__(self):
79 if self._module:
94 if self._module:
80 return "<proxied module '%s'>" % self._data[0]
95 return "<proxied module '%s'>" % self._data[0]
81 return "<unloaded module '%s'>" % self._data[0]
96 return "<unloaded module '%s'>" % self._data[0]
82 def __call__(self, *args, **kwargs):
97 def __call__(self, *args, **kwargs):
83 raise TypeError("%s object is not callable" % repr(self))
98 raise TypeError("%s object is not callable" % repr(self))
84 def __getattribute__(self, attr):
99 def __getattribute__(self, attr):
85 if attr in ('_data', '_extend', '_load', '_module'):
100 if attr in ('_data', '_extend', '_load', '_module'):
86 return object.__getattribute__(self, attr)
101 return object.__getattribute__(self, attr)
87 self._load()
102 self._load()
88 return getattr(self._module, attr)
103 return getattr(self._module, attr)
89 def __setattr__(self, attr, val):
104 def __setattr__(self, attr, val):
90 self._load()
105 self._load()
91 setattr(self._module, attr, val)
106 setattr(self._module, attr, val)
92
107
93 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
108 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
94 if not locals or name in ignore or fromlist == ('*',):
109 if not locals or name in ignore or fromlist == ('*',):
95 # these cases we can't really delay
110 # these cases we can't really delay
96 return _import(name, globals, locals, fromlist, level)
111 return _hgextimport(_import, name, globals, locals, fromlist, level)
97 elif not fromlist:
112 elif not fromlist:
98 # import a [as b]
113 # import a [as b]
99 if '.' in name: # a.b
114 if '.' in name: # a.b
100 base, rest = name.split('.', 1)
115 base, rest = name.split('.', 1)
101 # email.__init__ loading email.mime
116 # email.__init__ loading email.mime
102 if globals and globals.get('__name__', None) == base:
117 if globals and globals.get('__name__', None) == base:
103 return _import(name, globals, locals, fromlist, level)
118 return _import(name, globals, locals, fromlist, level)
104 # if a is already demand-loaded, add b to its submodule list
119 # if a is already demand-loaded, add b to its submodule list
105 if base in locals:
120 if base in locals:
106 if isinstance(locals[base], _demandmod):
121 if isinstance(locals[base], _demandmod):
107 locals[base]._extend(rest)
122 locals[base]._extend(rest)
108 return locals[base]
123 return locals[base]
109 return _demandmod(name, globals, locals, level)
124 return _demandmod(name, globals, locals, level)
110 else:
125 else:
111 if level != -1:
126 if level != -1:
112 # from . import b,c,d or from .a import b,c,d
127 # from . import b,c,d or from .a import b,c,d
113 return _origimport(name, globals, locals, fromlist, level)
128 return _origimport(name, globals, locals, fromlist, level)
114 # from a import b,c,d
129 # from a import b,c,d
115 mod = _origimport(name, globals, locals)
130 mod = _hgextimport(_origimport, name, globals, locals)
116 # recurse down the module chain
131 # recurse down the module chain
117 for comp in name.split('.')[1:]:
132 for comp in name.split('.')[1:]:
118 if getattr(mod, comp, nothing) is nothing:
133 if getattr(mod, comp, nothing) is nothing:
119 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
134 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
120 mod = getattr(mod, comp)
135 mod = getattr(mod, comp)
121 for x in fromlist:
136 for x in fromlist:
122 # set requested submodules for demand load
137 # set requested submodules for demand load
123 if getattr(mod, x, nothing) is nothing:
138 if getattr(mod, x, nothing) is nothing:
124 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
139 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
125 return mod
140 return mod
126
141
127 ignore = [
142 ignore = [
128 '_hashlib',
143 '_hashlib',
129 '_xmlplus',
144 '_xmlplus',
130 'fcntl',
145 'fcntl',
131 'win32com.gen_py',
146 'win32com.gen_py',
132 '_winreg', # 2.7 mimetypes needs immediate ImportError
147 '_winreg', # 2.7 mimetypes needs immediate ImportError
133 'pythoncom',
148 'pythoncom',
134 # imported by tarfile, not available under Windows
149 # imported by tarfile, not available under Windows
135 'pwd',
150 'pwd',
136 'grp',
151 'grp',
137 # imported by profile, itself imported by hotshot.stats,
152 # imported by profile, itself imported by hotshot.stats,
138 # not available under Windows
153 # not available under Windows
139 'resource',
154 'resource',
140 # this trips up many extension authors
155 # this trips up many extension authors
141 'gtk',
156 'gtk',
142 # setuptools' pkg_resources.py expects "from __main__ import x" to
157 # setuptools' pkg_resources.py expects "from __main__ import x" to
143 # raise ImportError if x not defined
158 # raise ImportError if x not defined
144 '__main__',
159 '__main__',
145 '_ssl', # conditional imports in the stdlib, issue1964
160 '_ssl', # conditional imports in the stdlib, issue1964
146 'rfc822',
161 'rfc822',
147 'mimetools',
162 'mimetools',
148 ]
163 ]
149
164
150 def enable():
165 def enable():
151 "enable global demand-loading of modules"
166 "enable global demand-loading of modules"
152 __builtin__.__import__ = _demandimport
167 __builtin__.__import__ = _demandimport
153
168
154 def disable():
169 def disable():
155 "disable global demand-loading of modules"
170 "disable global demand-loading of modules"
156 __builtin__.__import__ = _origimport
171 __builtin__.__import__ = _origimport
@@ -1,749 +1,840
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import commands
5 > from mercurial import commands
6 >
6 >
7 > def uisetup(ui):
7 > def uisetup(ui):
8 > ui.write("uisetup called\\n")
8 > ui.write("uisetup called\\n")
9 >
9 >
10 > def reposetup(ui, repo):
10 > def reposetup(ui, repo):
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 >
13 >
14 > def foo(ui, *args, **kwargs):
14 > def foo(ui, *args, **kwargs):
15 > ui.write("Foo\\n")
15 > ui.write("Foo\\n")
16 >
16 >
17 > def bar(ui, *args, **kwargs):
17 > def bar(ui, *args, **kwargs):
18 > ui.write("Bar\\n")
18 > ui.write("Bar\\n")
19 >
19 >
20 > cmdtable = {
20 > cmdtable = {
21 > "foo": (foo, [], "hg foo"),
21 > "foo": (foo, [], "hg foo"),
22 > "bar": (bar, [], "hg bar"),
22 > "bar": (bar, [], "hg bar"),
23 > }
23 > }
24 >
24 >
25 > commands.norepo += ' bar'
25 > commands.norepo += ' bar'
26 > EOF
26 > EOF
27 $ abspath=`pwd`/foobar.py
27 $ abspath=`pwd`/foobar.py
28
28
29 $ mkdir barfoo
29 $ mkdir barfoo
30 $ cp foobar.py barfoo/__init__.py
30 $ cp foobar.py barfoo/__init__.py
31 $ barfoopath=`pwd`/barfoo
31 $ barfoopath=`pwd`/barfoo
32
32
33 $ hg init a
33 $ hg init a
34 $ cd a
34 $ cd a
35 $ echo foo > file
35 $ echo foo > file
36 $ hg add file
36 $ hg add file
37 $ hg commit -m 'add file'
37 $ hg commit -m 'add file'
38
38
39 $ echo '[extensions]' >> $HGRCPATH
39 $ echo '[extensions]' >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
40 $ echo "foobar = $abspath" >> $HGRCPATH
41 $ hg foo
41 $ hg foo
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 Foo
45 Foo
46
46
47 $ cd ..
47 $ cd ..
48 $ hg clone a b
48 $ hg clone a b
49 uisetup called
49 uisetup called
50 reposetup called for a
50 reposetup called for a
51 ui == repo.ui
51 ui == repo.ui
52 reposetup called for b
52 reposetup called for b
53 ui == repo.ui
53 ui == repo.ui
54 updating to branch default
54 updating to branch default
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56
56
57 $ hg bar
57 $ hg bar
58 uisetup called
58 uisetup called
59 Bar
59 Bar
60 $ echo 'foobar = !' >> $HGRCPATH
60 $ echo 'foobar = !' >> $HGRCPATH
61
61
62 module/__init__.py-style
62 module/__init__.py-style
63
63
64 $ echo "barfoo = $barfoopath" >> $HGRCPATH
64 $ echo "barfoo = $barfoopath" >> $HGRCPATH
65 $ cd a
65 $ cd a
66 $ hg foo
66 $ hg foo
67 uisetup called
67 uisetup called
68 reposetup called for a
68 reposetup called for a
69 ui == repo.ui
69 ui == repo.ui
70 Foo
70 Foo
71 $ echo 'barfoo = !' >> $HGRCPATH
71 $ echo 'barfoo = !' >> $HGRCPATH
72
72
73 Check that extensions are loaded in phases:
73 Check that extensions are loaded in phases:
74
74
75 $ cat > foo.py <<EOF
75 $ cat > foo.py <<EOF
76 > import os
76 > import os
77 > name = os.path.basename(__file__).rsplit('.', 1)[0]
77 > name = os.path.basename(__file__).rsplit('.', 1)[0]
78 > print "1) %s imported" % name
78 > print "1) %s imported" % name
79 > def uisetup(ui):
79 > def uisetup(ui):
80 > print "2) %s uisetup" % name
80 > print "2) %s uisetup" % name
81 > def extsetup():
81 > def extsetup():
82 > print "3) %s extsetup" % name
82 > print "3) %s extsetup" % name
83 > def reposetup(ui, repo):
83 > def reposetup(ui, repo):
84 > print "4) %s reposetup" % name
84 > print "4) %s reposetup" % name
85 > EOF
85 > EOF
86
86
87 $ cp foo.py bar.py
87 $ cp foo.py bar.py
88 $ echo 'foo = foo.py' >> $HGRCPATH
88 $ echo 'foo = foo.py' >> $HGRCPATH
89 $ echo 'bar = bar.py' >> $HGRCPATH
89 $ echo 'bar = bar.py' >> $HGRCPATH
90
90
91 Command with no output, we just want to see the extensions loaded:
91 Command with no output, we just want to see the extensions loaded:
92
92
93 $ hg paths
93 $ hg paths
94 1) foo imported
94 1) foo imported
95 1) bar imported
95 1) bar imported
96 2) foo uisetup
96 2) foo uisetup
97 2) bar uisetup
97 2) bar uisetup
98 3) foo extsetup
98 3) foo extsetup
99 3) bar extsetup
99 3) bar extsetup
100 4) foo reposetup
100 4) foo reposetup
101 4) bar reposetup
101 4) bar reposetup
102
102
103 Check hgweb's load order:
103 Check hgweb's load order:
104
104
105 $ cat > hgweb.cgi <<EOF
105 $ cat > hgweb.cgi <<EOF
106 > #!/usr/bin/env python
106 > #!/usr/bin/env python
107 > from mercurial import demandimport; demandimport.enable()
107 > from mercurial import demandimport; demandimport.enable()
108 > from mercurial.hgweb import hgweb
108 > from mercurial.hgweb import hgweb
109 > from mercurial.hgweb import wsgicgi
109 > from mercurial.hgweb import wsgicgi
110 >
110 >
111 > application = hgweb('.', 'test repo')
111 > application = hgweb('.', 'test repo')
112 > wsgicgi.launch(application)
112 > wsgicgi.launch(application)
113 > EOF
113 > EOF
114
114
115 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
115 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
116 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
116 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
117 > | grep '^[0-9]) ' # ignores HTML output
117 > | grep '^[0-9]) ' # ignores HTML output
118 1) foo imported
118 1) foo imported
119 1) bar imported
119 1) bar imported
120 2) foo uisetup
120 2) foo uisetup
121 2) bar uisetup
121 2) bar uisetup
122 3) foo extsetup
122 3) foo extsetup
123 3) bar extsetup
123 3) bar extsetup
124 4) foo reposetup
124 4) foo reposetup
125 4) bar reposetup
125 4) bar reposetup
126 4) foo reposetup
126 4) foo reposetup
127 4) bar reposetup
127 4) bar reposetup
128
128
129 $ echo 'foo = !' >> $HGRCPATH
129 $ echo 'foo = !' >> $HGRCPATH
130 $ echo 'bar = !' >> $HGRCPATH
130 $ echo 'bar = !' >> $HGRCPATH
131
131
132 Check "from __future__ import absolute_import" support for external libraries
132 Check "from __future__ import absolute_import" support for external libraries
133
133
134 $ mkdir $TESTTMP/libroot
134 $ mkdir $TESTTMP/libroot
135 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
135 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
136 $ mkdir $TESTTMP/libroot/mod
136 $ mkdir $TESTTMP/libroot/mod
137 $ touch $TESTTMP/libroot/mod/__init__.py
137 $ touch $TESTTMP/libroot/mod/__init__.py
138 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
138 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
139
139
140 #if absimport
140 #if absimport
141 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
141 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
142 > from __future__ import absolute_import
142 > from __future__ import absolute_import
143 > import ambig # should load "libroot/ambig.py"
143 > import ambig # should load "libroot/ambig.py"
144 > s = ambig.s
144 > s = ambig.s
145 > EOF
145 > EOF
146 $ cat > loadabs.py <<EOF
146 $ cat > loadabs.py <<EOF
147 > import mod.ambigabs as ambigabs
147 > import mod.ambigabs as ambigabs
148 > def extsetup():
148 > def extsetup():
149 > print 'ambigabs.s=%s' % ambigabs.s
149 > print 'ambigabs.s=%s' % ambigabs.s
150 > EOF
150 > EOF
151 $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadabs=loadabs.py root)
151 $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadabs=loadabs.py root)
152 ambigabs.s=libroot/ambig.py
152 ambigabs.s=libroot/ambig.py
153 $TESTTMP/a
153 $TESTTMP/a
154 #endif
154 #endif
155
155
156 #if no-py3k
156 #if no-py3k
157 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
157 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
158 > import ambig # should load "libroot/mod/ambig.py"
158 > import ambig # should load "libroot/mod/ambig.py"
159 > s = ambig.s
159 > s = ambig.s
160 > EOF
160 > EOF
161 $ cat > loadrel.py <<EOF
161 $ cat > loadrel.py <<EOF
162 > import mod.ambigrel as ambigrel
162 > import mod.ambigrel as ambigrel
163 > def extsetup():
163 > def extsetup():
164 > print 'ambigrel.s=%s' % ambigrel.s
164 > print 'ambigrel.s=%s' % ambigrel.s
165 > EOF
165 > EOF
166 $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadrel=loadrel.py root)
166 $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadrel=loadrel.py root)
167 ambigrel.s=libroot/mod/ambig.py
167 ambigrel.s=libroot/mod/ambig.py
168 $TESTTMP/a
168 $TESTTMP/a
169 #endif
169 #endif
170
170
171 Check absolute/relative import of extension specific modules
172
173 $ mkdir $TESTTMP/extroot
174 $ cat > $TESTTMP/extroot/bar.py <<EOF
175 > s = 'this is extroot.bar'
176 > EOF
177 $ mkdir $TESTTMP/extroot/sub1
178 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
179 > s = 'this is extroot.sub1.__init__'
180 > EOF
181 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
182 > s = 'this is extroot.sub1.baz'
183 > EOF
184 $ cat > $TESTTMP/extroot/__init__.py <<EOF
185 > s = 'this is extroot.__init__'
186 > import foo
187 > def extsetup(ui):
188 > ui.write('(extroot) ', foo.func(), '\n')
189 > EOF
190
191 $ cat > $TESTTMP/extroot/foo.py <<EOF
192 > # test absolute import
193 > buf = []
194 > def func():
195 > # "not locals" case
196 > import extroot.bar
197 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
198 >
199 > return '\n(extroot) '.join(buf)
200 >
201 > # "fromlist == ('*',)" case
202 > from extroot.bar import *
203 > buf.append('from extroot.bar import *: %s' % s)
204 >
205 > # "not fromlist" and "if '.' in name" case
206 > import extroot.sub1.baz
207 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
208 >
209 > # "not fromlist" and NOT "if '.' in name" case
210 > import extroot
211 > buf.append('import extroot: %s' % extroot.s)
212 >
213 > # NOT "not fromlist" and NOT "level != -1" case
214 > from extroot.bar import s
215 > buf.append('from extroot.bar import s: %s' % s)
216 > EOF
217 $ hg --config extensions.extroot=$TESTTMP/extroot root
218 (extroot) from extroot.bar import *: this is extroot.bar
219 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
220 (extroot) import extroot: this is extroot.__init__
221 (extroot) from extroot.bar import s: this is extroot.bar
222 (extroot) import extroot.bar in func(): this is extroot.bar
223 $TESTTMP/a
224
225 #if no-py3k
226 $ rm -f $TESTTMP/extroot/foo.*
227 $ cat > $TESTTMP/extroot/foo.py <<EOF
228 > # test relative import
229 > buf = []
230 > def func():
231 > # "not locals" case
232 > import bar
233 > buf.append('import bar in func(): %s' % bar.s)
234 >
235 > return '\n(extroot) '.join(buf)
236 >
237 > # "fromlist == ('*',)" case
238 > from bar import *
239 > buf.append('from bar import *: %s' % s)
240 >
241 > # "not fromlist" and "if '.' in name" case
242 > import sub1.baz
243 > buf.append('import sub1.baz: %s' % sub1.baz.s)
244 >
245 > # "not fromlist" and NOT "if '.' in name" case
246 > import sub1
247 > buf.append('import sub1: %s' % sub1.s)
248 >
249 > # NOT "not fromlist" and NOT "level != -1" case
250 > from bar import s
251 > buf.append('from bar import s: %s' % s)
252 > EOF
253 $ hg --config extensions.extroot=$TESTTMP/extroot root
254 (extroot) from bar import *: this is extroot.bar
255 (extroot) import sub1.baz: this is extroot.sub1.baz
256 (extroot) import sub1: this is extroot.sub1.__init__
257 (extroot) from bar import s: this is extroot.bar
258 (extroot) import bar in func(): this is extroot.bar
259 $TESTTMP/a
260 #endif
261
171 $ cd ..
262 $ cd ..
172
263
173 hide outer repo
264 hide outer repo
174 $ hg init
265 $ hg init
175
266
176 $ cat > empty.py <<EOF
267 $ cat > empty.py <<EOF
177 > '''empty cmdtable
268 > '''empty cmdtable
178 > '''
269 > '''
179 > cmdtable = {}
270 > cmdtable = {}
180 > EOF
271 > EOF
181 $ emptypath=`pwd`/empty.py
272 $ emptypath=`pwd`/empty.py
182 $ echo "empty = $emptypath" >> $HGRCPATH
273 $ echo "empty = $emptypath" >> $HGRCPATH
183 $ hg help empty
274 $ hg help empty
184 empty extension - empty cmdtable
275 empty extension - empty cmdtable
185
276
186 no commands defined
277 no commands defined
187
278
188 $ echo 'empty = !' >> $HGRCPATH
279 $ echo 'empty = !' >> $HGRCPATH
189
280
190 $ cat > debugextension.py <<EOF
281 $ cat > debugextension.py <<EOF
191 > '''only debugcommands
282 > '''only debugcommands
192 > '''
283 > '''
193 > def debugfoobar(ui, repo, *args, **opts):
284 > def debugfoobar(ui, repo, *args, **opts):
194 > "yet another debug command"
285 > "yet another debug command"
195 > pass
286 > pass
196 >
287 >
197 > def foo(ui, repo, *args, **opts):
288 > def foo(ui, repo, *args, **opts):
198 > """yet another foo command
289 > """yet another foo command
199 >
290 >
200 > This command has been DEPRECATED since forever.
291 > This command has been DEPRECATED since forever.
201 > """
292 > """
202 > pass
293 > pass
203 >
294 >
204 > cmdtable = {
295 > cmdtable = {
205 > "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
296 > "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
206 > "foo": (foo, (), "hg foo")
297 > "foo": (foo, (), "hg foo")
207 > }
298 > }
208 > EOF
299 > EOF
209 $ debugpath=`pwd`/debugextension.py
300 $ debugpath=`pwd`/debugextension.py
210 $ echo "debugextension = $debugpath" >> $HGRCPATH
301 $ echo "debugextension = $debugpath" >> $HGRCPATH
211
302
212 $ hg help debugextension
303 $ hg help debugextension
213 debugextension extension - only debugcommands
304 debugextension extension - only debugcommands
214
305
215 no commands defined
306 no commands defined
216
307
217 $ hg --verbose help debugextension
308 $ hg --verbose help debugextension
218 debugextension extension - only debugcommands
309 debugextension extension - only debugcommands
219
310
220 list of commands:
311 list of commands:
221
312
222 foo yet another foo command
313 foo yet another foo command
223
314
224 global options:
315 global options:
225
316
226 -R --repository REPO repository root directory or name of overlay bundle
317 -R --repository REPO repository root directory or name of overlay bundle
227 file
318 file
228 --cwd DIR change working directory
319 --cwd DIR change working directory
229 -y --noninteractive do not prompt, automatically pick the first choice for
320 -y --noninteractive do not prompt, automatically pick the first choice for
230 all prompts
321 all prompts
231 -q --quiet suppress output
322 -q --quiet suppress output
232 -v --verbose enable additional output
323 -v --verbose enable additional output
233 --config CONFIG [+] set/override config option (use 'section.name=value')
324 --config CONFIG [+] set/override config option (use 'section.name=value')
234 --debug enable debugging output
325 --debug enable debugging output
235 --debugger start debugger
326 --debugger start debugger
236 --encoding ENCODE set the charset encoding (default: ascii)
327 --encoding ENCODE set the charset encoding (default: ascii)
237 --encodingmode MODE set the charset encoding mode (default: strict)
328 --encodingmode MODE set the charset encoding mode (default: strict)
238 --traceback always print a traceback on exception
329 --traceback always print a traceback on exception
239 --time time how long the command takes
330 --time time how long the command takes
240 --profile print command execution profile
331 --profile print command execution profile
241 --version output version information and exit
332 --version output version information and exit
242 -h --help display help and exit
333 -h --help display help and exit
243 --hidden consider hidden changesets
334 --hidden consider hidden changesets
244
335
245 [+] marked option can be specified multiple times
336 [+] marked option can be specified multiple times
246
337
247 $ hg --debug help debugextension
338 $ hg --debug help debugextension
248 debugextension extension - only debugcommands
339 debugextension extension - only debugcommands
249
340
250 list of commands:
341 list of commands:
251
342
252 debugfoobar yet another debug command
343 debugfoobar yet another debug command
253 foo yet another foo command
344 foo yet another foo command
254
345
255 global options:
346 global options:
256
347
257 -R --repository REPO repository root directory or name of overlay bundle
348 -R --repository REPO repository root directory or name of overlay bundle
258 file
349 file
259 --cwd DIR change working directory
350 --cwd DIR change working directory
260 -y --noninteractive do not prompt, automatically pick the first choice for
351 -y --noninteractive do not prompt, automatically pick the first choice for
261 all prompts
352 all prompts
262 -q --quiet suppress output
353 -q --quiet suppress output
263 -v --verbose enable additional output
354 -v --verbose enable additional output
264 --config CONFIG [+] set/override config option (use 'section.name=value')
355 --config CONFIG [+] set/override config option (use 'section.name=value')
265 --debug enable debugging output
356 --debug enable debugging output
266 --debugger start debugger
357 --debugger start debugger
267 --encoding ENCODE set the charset encoding (default: ascii)
358 --encoding ENCODE set the charset encoding (default: ascii)
268 --encodingmode MODE set the charset encoding mode (default: strict)
359 --encodingmode MODE set the charset encoding mode (default: strict)
269 --traceback always print a traceback on exception
360 --traceback always print a traceback on exception
270 --time time how long the command takes
361 --time time how long the command takes
271 --profile print command execution profile
362 --profile print command execution profile
272 --version output version information and exit
363 --version output version information and exit
273 -h --help display help and exit
364 -h --help display help and exit
274 --hidden consider hidden changesets
365 --hidden consider hidden changesets
275
366
276 [+] marked option can be specified multiple times
367 [+] marked option can be specified multiple times
277 $ echo 'debugextension = !' >> $HGRCPATH
368 $ echo 'debugextension = !' >> $HGRCPATH
278
369
279 Extension module help vs command help:
370 Extension module help vs command help:
280
371
281 $ echo 'extdiff =' >> $HGRCPATH
372 $ echo 'extdiff =' >> $HGRCPATH
282 $ hg help extdiff
373 $ hg help extdiff
283 hg extdiff [OPT]... [FILE]...
374 hg extdiff [OPT]... [FILE]...
284
375
285 use external program to diff repository (or selected files)
376 use external program to diff repository (or selected files)
286
377
287 Show differences between revisions for the specified files, using an
378 Show differences between revisions for the specified files, using an
288 external program. The default program used is diff, with default options
379 external program. The default program used is diff, with default options
289 "-Npru".
380 "-Npru".
290
381
291 To select a different program, use the -p/--program option. The program
382 To select a different program, use the -p/--program option. The program
292 will be passed the names of two directories to compare. To pass additional
383 will be passed the names of two directories to compare. To pass additional
293 options to the program, use -o/--option. These will be passed before the
384 options to the program, use -o/--option. These will be passed before the
294 names of the directories to compare.
385 names of the directories to compare.
295
386
296 When two revision arguments are given, then changes are shown between
387 When two revision arguments are given, then changes are shown between
297 those revisions. If only one revision is specified then that revision is
388 those revisions. If only one revision is specified then that revision is
298 compared to the working directory, and, when no revisions are specified,
389 compared to the working directory, and, when no revisions are specified,
299 the working directory files are compared to its parent.
390 the working directory files are compared to its parent.
300
391
301 use "hg help -e extdiff" to show help for the extdiff extension
392 use "hg help -e extdiff" to show help for the extdiff extension
302
393
303 options:
394 options:
304
395
305 -p --program CMD comparison program to run
396 -p --program CMD comparison program to run
306 -o --option OPT [+] pass option to comparison program
397 -o --option OPT [+] pass option to comparison program
307 -r --rev REV [+] revision
398 -r --rev REV [+] revision
308 -c --change REV change made by revision
399 -c --change REV change made by revision
309 -I --include PATTERN [+] include names matching the given patterns
400 -I --include PATTERN [+] include names matching the given patterns
310 -X --exclude PATTERN [+] exclude names matching the given patterns
401 -X --exclude PATTERN [+] exclude names matching the given patterns
311
402
312 [+] marked option can be specified multiple times
403 [+] marked option can be specified multiple times
313
404
314 use "hg -v help extdiff" to show the global options
405 use "hg -v help extdiff" to show the global options
315
406
316 $ hg help --extension extdiff
407 $ hg help --extension extdiff
317 extdiff extension - command to allow external programs to compare revisions
408 extdiff extension - command to allow external programs to compare revisions
318
409
319 The extdiff Mercurial extension allows you to use external programs to compare
410 The extdiff Mercurial extension allows you to use external programs to compare
320 revisions, or revision with working directory. The external diff programs are
411 revisions, or revision with working directory. The external diff programs are
321 called with a configurable set of options and two non-option arguments: paths
412 called with a configurable set of options and two non-option arguments: paths
322 to directories containing snapshots of files to compare.
413 to directories containing snapshots of files to compare.
323
414
324 The extdiff extension also allows you to configure new diff commands, so you
415 The extdiff extension also allows you to configure new diff commands, so you
325 do not need to type "hg extdiff -p kdiff3" always.
416 do not need to type "hg extdiff -p kdiff3" always.
326
417
327 [extdiff]
418 [extdiff]
328 # add new command that runs GNU diff(1) in 'context diff' mode
419 # add new command that runs GNU diff(1) in 'context diff' mode
329 cdiff = gdiff -Nprc5
420 cdiff = gdiff -Nprc5
330 ## or the old way:
421 ## or the old way:
331 #cmd.cdiff = gdiff
422 #cmd.cdiff = gdiff
332 #opts.cdiff = -Nprc5
423 #opts.cdiff = -Nprc5
333
424
334 # add new command called vdiff, runs kdiff3
425 # add new command called vdiff, runs kdiff3
335 vdiff = kdiff3
426 vdiff = kdiff3
336
427
337 # add new command called meld, runs meld (no need to name twice)
428 # add new command called meld, runs meld (no need to name twice)
338 meld =
429 meld =
339
430
340 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
431 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
341 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
432 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
342 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
433 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
343 # your .vimrc
434 # your .vimrc
344 vimdiff = gvim -f "+next" \
435 vimdiff = gvim -f "+next" \
345 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
436 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
346
437
347 Tool arguments can include variables that are expanded at runtime:
438 Tool arguments can include variables that are expanded at runtime:
348
439
349 $parent1, $plabel1 - filename, descriptive label of first parent
440 $parent1, $plabel1 - filename, descriptive label of first parent
350 $child, $clabel - filename, descriptive label of child revision
441 $child, $clabel - filename, descriptive label of child revision
351 $parent2, $plabel2 - filename, descriptive label of second parent
442 $parent2, $plabel2 - filename, descriptive label of second parent
352 $root - repository root
443 $root - repository root
353 $parent is an alias for $parent1.
444 $parent is an alias for $parent1.
354
445
355 The extdiff extension will look in your [diff-tools] and [merge-tools]
446 The extdiff extension will look in your [diff-tools] and [merge-tools]
356 sections for diff tool arguments, when none are specified in [extdiff].
447 sections for diff tool arguments, when none are specified in [extdiff].
357
448
358 [extdiff]
449 [extdiff]
359 kdiff3 =
450 kdiff3 =
360
451
361 [diff-tools]
452 [diff-tools]
362 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
453 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
363
454
364 You can use -I/-X and list of file or directory names like normal "hg diff"
455 You can use -I/-X and list of file or directory names like normal "hg diff"
365 command. The extdiff extension makes snapshots of only needed files, so
456 command. The extdiff extension makes snapshots of only needed files, so
366 running the external diff program will actually be pretty fast (at least
457 running the external diff program will actually be pretty fast (at least
367 faster than having to compare the entire tree).
458 faster than having to compare the entire tree).
368
459
369 list of commands:
460 list of commands:
370
461
371 extdiff use external program to diff repository (or selected files)
462 extdiff use external program to diff repository (or selected files)
372
463
373 use "hg -v help extdiff" to show builtin aliases and global options
464 use "hg -v help extdiff" to show builtin aliases and global options
374
465
375 $ echo 'extdiff = !' >> $HGRCPATH
466 $ echo 'extdiff = !' >> $HGRCPATH
376
467
377 Test help topic with same name as extension
468 Test help topic with same name as extension
378
469
379 $ cat > multirevs.py <<EOF
470 $ cat > multirevs.py <<EOF
380 > from mercurial import commands
471 > from mercurial import commands
381 > """multirevs extension
472 > """multirevs extension
382 > Big multi-line module docstring."""
473 > Big multi-line module docstring."""
383 > def multirevs(ui, repo, arg, *args, **opts):
474 > def multirevs(ui, repo, arg, *args, **opts):
384 > """multirevs command"""
475 > """multirevs command"""
385 > pass
476 > pass
386 > cmdtable = {
477 > cmdtable = {
387 > "multirevs": (multirevs, [], 'ARG')
478 > "multirevs": (multirevs, [], 'ARG')
388 > }
479 > }
389 > commands.norepo += ' multirevs'
480 > commands.norepo += ' multirevs'
390 > EOF
481 > EOF
391 $ echo "multirevs = multirevs.py" >> $HGRCPATH
482 $ echo "multirevs = multirevs.py" >> $HGRCPATH
392
483
393 $ hg help multirevs
484 $ hg help multirevs
394 Specifying Multiple Revisions
485 Specifying Multiple Revisions
395 """""""""""""""""""""""""""""
486 """""""""""""""""""""""""""""
396
487
397 When Mercurial accepts more than one revision, they may be specified
488 When Mercurial accepts more than one revision, they may be specified
398 individually, or provided as a topologically continuous range, separated
489 individually, or provided as a topologically continuous range, separated
399 by the ":" character.
490 by the ":" character.
400
491
401 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
492 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
402 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
493 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
403 specified, it defaults to revision number 0. If END is not specified, it
494 specified, it defaults to revision number 0. If END is not specified, it
404 defaults to the tip. The range ":" thus means "all revisions".
495 defaults to the tip. The range ":" thus means "all revisions".
405
496
406 If BEGIN is greater than END, revisions are treated in reverse order.
497 If BEGIN is greater than END, revisions are treated in reverse order.
407
498
408 A range acts as a closed interval. This means that a range of 3:5 gives 3,
499 A range acts as a closed interval. This means that a range of 3:5 gives 3,
409 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
500 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
410
501
411 use "hg help -c multirevs" to see help for the multirevs command
502 use "hg help -c multirevs" to see help for the multirevs command
412
503
413 $ hg help -c multirevs
504 $ hg help -c multirevs
414 hg multirevs ARG
505 hg multirevs ARG
415
506
416 multirevs command
507 multirevs command
417
508
418 use "hg -v help multirevs" to show the global options
509 use "hg -v help multirevs" to show the global options
419
510
420 $ hg multirevs
511 $ hg multirevs
421 hg multirevs: invalid arguments
512 hg multirevs: invalid arguments
422 hg multirevs ARG
513 hg multirevs ARG
423
514
424 multirevs command
515 multirevs command
425
516
426 use "hg help multirevs" to show the full help text
517 use "hg help multirevs" to show the full help text
427 [255]
518 [255]
428
519
429 $ echo "multirevs = !" >> $HGRCPATH
520 $ echo "multirevs = !" >> $HGRCPATH
430
521
431 Issue811: Problem loading extensions twice (by site and by user)
522 Issue811: Problem loading extensions twice (by site and by user)
432
523
433 $ debugpath=`pwd`/debugissue811.py
524 $ debugpath=`pwd`/debugissue811.py
434 $ cat > debugissue811.py <<EOF
525 $ cat > debugissue811.py <<EOF
435 > '''show all loaded extensions
526 > '''show all loaded extensions
436 > '''
527 > '''
437 > from mercurial import extensions, commands
528 > from mercurial import extensions, commands
438 >
529 >
439 > def debugextensions(ui):
530 > def debugextensions(ui):
440 > "yet another debug command"
531 > "yet another debug command"
441 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
532 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
442 >
533 >
443 > cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
534 > cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
444 > commands.norepo += " debugextensions"
535 > commands.norepo += " debugextensions"
445 > EOF
536 > EOF
446 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
537 $ echo "debugissue811 = $debugpath" >> $HGRCPATH
447 $ echo "mq=" >> $HGRCPATH
538 $ echo "mq=" >> $HGRCPATH
448 $ echo "strip=" >> $HGRCPATH
539 $ echo "strip=" >> $HGRCPATH
449 $ echo "hgext.mq=" >> $HGRCPATH
540 $ echo "hgext.mq=" >> $HGRCPATH
450 $ echo "hgext/mq=" >> $HGRCPATH
541 $ echo "hgext/mq=" >> $HGRCPATH
451
542
452 Show extensions:
543 Show extensions:
453 (note that mq force load strip, also checking it's not loaded twice)
544 (note that mq force load strip, also checking it's not loaded twice)
454
545
455 $ hg debugextensions
546 $ hg debugextensions
456 debugissue811
547 debugissue811
457 strip
548 strip
458 mq
549 mq
459
550
460 Disabled extension commands:
551 Disabled extension commands:
461
552
462 $ ORGHGRCPATH=$HGRCPATH
553 $ ORGHGRCPATH=$HGRCPATH
463 $ HGRCPATH=
554 $ HGRCPATH=
464 $ export HGRCPATH
555 $ export HGRCPATH
465 $ hg help email
556 $ hg help email
466 'email' is provided by the following extension:
557 'email' is provided by the following extension:
467
558
468 patchbomb command to send changesets as (a series of) patch emails
559 patchbomb command to send changesets as (a series of) patch emails
469
560
470 use "hg help extensions" for information on enabling extensions
561 use "hg help extensions" for information on enabling extensions
471 $ hg qdel
562 $ hg qdel
472 hg: unknown command 'qdel'
563 hg: unknown command 'qdel'
473 'qdelete' is provided by the following extension:
564 'qdelete' is provided by the following extension:
474
565
475 mq manage a stack of patches
566 mq manage a stack of patches
476
567
477 use "hg help extensions" for information on enabling extensions
568 use "hg help extensions" for information on enabling extensions
478 [255]
569 [255]
479 $ hg churn
570 $ hg churn
480 hg: unknown command 'churn'
571 hg: unknown command 'churn'
481 'churn' is provided by the following extension:
572 'churn' is provided by the following extension:
482
573
483 churn command to display statistics about repository history
574 churn command to display statistics about repository history
484
575
485 use "hg help extensions" for information on enabling extensions
576 use "hg help extensions" for information on enabling extensions
486 [255]
577 [255]
487
578
488 Disabled extensions:
579 Disabled extensions:
489
580
490 $ hg help churn
581 $ hg help churn
491 churn extension - command to display statistics about repository history
582 churn extension - command to display statistics about repository history
492
583
493 use "hg help extensions" for information on enabling extensions
584 use "hg help extensions" for information on enabling extensions
494 $ hg help patchbomb
585 $ hg help patchbomb
495 patchbomb extension - command to send changesets as (a series of) patch emails
586 patchbomb extension - command to send changesets as (a series of) patch emails
496
587
497 use "hg help extensions" for information on enabling extensions
588 use "hg help extensions" for information on enabling extensions
498
589
499 Broken disabled extension and command:
590 Broken disabled extension and command:
500
591
501 $ mkdir hgext
592 $ mkdir hgext
502 $ echo > hgext/__init__.py
593 $ echo > hgext/__init__.py
503 $ cat > hgext/broken.py <<EOF
594 $ cat > hgext/broken.py <<EOF
504 > "broken extension'
595 > "broken extension'
505 > EOF
596 > EOF
506 $ cat > path.py <<EOF
597 $ cat > path.py <<EOF
507 > import os, sys
598 > import os, sys
508 > sys.path.insert(0, os.environ['HGEXTPATH'])
599 > sys.path.insert(0, os.environ['HGEXTPATH'])
509 > EOF
600 > EOF
510 $ HGEXTPATH=`pwd`
601 $ HGEXTPATH=`pwd`
511 $ export HGEXTPATH
602 $ export HGEXTPATH
512
603
513 $ hg --config extensions.path=./path.py help broken
604 $ hg --config extensions.path=./path.py help broken
514 broken extension - (no help text available)
605 broken extension - (no help text available)
515
606
516 use "hg help extensions" for information on enabling extensions
607 use "hg help extensions" for information on enabling extensions
517
608
518 $ cat > hgext/forest.py <<EOF
609 $ cat > hgext/forest.py <<EOF
519 > cmdtable = None
610 > cmdtable = None
520 > EOF
611 > EOF
521 $ hg --config extensions.path=./path.py help foo > /dev/null
612 $ hg --config extensions.path=./path.py help foo > /dev/null
522 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
613 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
523 hg: unknown command 'foo'
614 hg: unknown command 'foo'
524 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
615 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
525 [255]
616 [255]
526
617
527 $ cat > throw.py <<EOF
618 $ cat > throw.py <<EOF
528 > from mercurial import cmdutil, commands
619 > from mercurial import cmdutil, commands
529 > cmdtable = {}
620 > cmdtable = {}
530 > command = cmdutil.command(cmdtable)
621 > command = cmdutil.command(cmdtable)
531 > class Bogon(Exception): pass
622 > class Bogon(Exception): pass
532 >
623 >
533 > @command('throw', [], 'hg throw')
624 > @command('throw', [], 'hg throw')
534 > def throw(ui, **opts):
625 > def throw(ui, **opts):
535 > """throws an exception"""
626 > """throws an exception"""
536 > raise Bogon()
627 > raise Bogon()
537 > commands.norepo += " throw"
628 > commands.norepo += " throw"
538 > EOF
629 > EOF
539 No declared supported version, extension complains:
630 No declared supported version, extension complains:
540 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
631 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
541 ** Unknown exception encountered with possibly-broken third-party extension throw
632 ** Unknown exception encountered with possibly-broken third-party extension throw
542 ** which supports versions unknown of Mercurial.
633 ** which supports versions unknown of Mercurial.
543 ** Please disable throw and try your action again.
634 ** Please disable throw and try your action again.
544 ** If that fixes the bug please report it to the extension author.
635 ** If that fixes the bug please report it to the extension author.
545 ** Python * (glob)
636 ** Python * (glob)
546 ** Mercurial Distributed SCM * (glob)
637 ** Mercurial Distributed SCM * (glob)
547 ** Extensions loaded: throw
638 ** Extensions loaded: throw
548 empty declaration of supported version, extension complains:
639 empty declaration of supported version, extension complains:
549 $ echo "testedwith = ''" >> throw.py
640 $ echo "testedwith = ''" >> throw.py
550 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
641 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
551 ** Unknown exception encountered with possibly-broken third-party extension throw
642 ** Unknown exception encountered with possibly-broken third-party extension throw
552 ** which supports versions unknown of Mercurial.
643 ** which supports versions unknown of Mercurial.
553 ** Please disable throw and try your action again.
644 ** Please disable throw and try your action again.
554 ** If that fixes the bug please report it to the extension author.
645 ** If that fixes the bug please report it to the extension author.
555 ** Python * (glob)
646 ** Python * (glob)
556 ** Mercurial Distributed SCM (*) (glob)
647 ** Mercurial Distributed SCM (*) (glob)
557 ** Extensions loaded: throw
648 ** Extensions loaded: throw
558 If the extension specifies a buglink, show that:
649 If the extension specifies a buglink, show that:
559 $ echo 'buglink = "http://example.com/bts"' >> throw.py
650 $ echo 'buglink = "http://example.com/bts"' >> throw.py
560 $ rm -f throw.pyc throw.pyo
651 $ rm -f throw.pyc throw.pyo
561 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
652 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
562 ** Unknown exception encountered with possibly-broken third-party extension throw
653 ** Unknown exception encountered with possibly-broken third-party extension throw
563 ** which supports versions unknown of Mercurial.
654 ** which supports versions unknown of Mercurial.
564 ** Please disable throw and try your action again.
655 ** Please disable throw and try your action again.
565 ** If that fixes the bug please report it to http://example.com/bts
656 ** If that fixes the bug please report it to http://example.com/bts
566 ** Python * (glob)
657 ** Python * (glob)
567 ** Mercurial Distributed SCM (*) (glob)
658 ** Mercurial Distributed SCM (*) (glob)
568 ** Extensions loaded: throw
659 ** Extensions loaded: throw
569 If the extensions declare outdated versions, accuse the older extension first:
660 If the extensions declare outdated versions, accuse the older extension first:
570 $ echo "from mercurial import util" >> older.py
661 $ echo "from mercurial import util" >> older.py
571 $ echo "util.version = lambda:'2.2'" >> older.py
662 $ echo "util.version = lambda:'2.2'" >> older.py
572 $ echo "testedwith = '1.9.3'" >> older.py
663 $ echo "testedwith = '1.9.3'" >> older.py
573 $ echo "testedwith = '2.1.1'" >> throw.py
664 $ echo "testedwith = '2.1.1'" >> throw.py
574 $ rm -f throw.pyc throw.pyo
665 $ rm -f throw.pyc throw.pyo
575 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
666 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
576 > throw 2>&1 | egrep '^\*\*'
667 > throw 2>&1 | egrep '^\*\*'
577 ** Unknown exception encountered with possibly-broken third-party extension older
668 ** Unknown exception encountered with possibly-broken third-party extension older
578 ** which supports versions 1.9.3 of Mercurial.
669 ** which supports versions 1.9.3 of Mercurial.
579 ** Please disable older and try your action again.
670 ** Please disable older and try your action again.
580 ** If that fixes the bug please report it to the extension author.
671 ** If that fixes the bug please report it to the extension author.
581 ** Python * (glob)
672 ** Python * (glob)
582 ** Mercurial Distributed SCM (version 2.2)
673 ** Mercurial Distributed SCM (version 2.2)
583 ** Extensions loaded: throw, older
674 ** Extensions loaded: throw, older
584 One extension only tested with older, one only with newer versions:
675 One extension only tested with older, one only with newer versions:
585 $ echo "util.version = lambda:'2.1.0'" >> older.py
676 $ echo "util.version = lambda:'2.1.0'" >> older.py
586 $ rm -f older.pyc older.pyo
677 $ rm -f older.pyc older.pyo
587 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
678 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
588 > throw 2>&1 | egrep '^\*\*'
679 > throw 2>&1 | egrep '^\*\*'
589 ** Unknown exception encountered with possibly-broken third-party extension older
680 ** Unknown exception encountered with possibly-broken third-party extension older
590 ** which supports versions 1.9.3 of Mercurial.
681 ** which supports versions 1.9.3 of Mercurial.
591 ** Please disable older and try your action again.
682 ** Please disable older and try your action again.
592 ** If that fixes the bug please report it to the extension author.
683 ** If that fixes the bug please report it to the extension author.
593 ** Python * (glob)
684 ** Python * (glob)
594 ** Mercurial Distributed SCM (version 2.1.0)
685 ** Mercurial Distributed SCM (version 2.1.0)
595 ** Extensions loaded: throw, older
686 ** Extensions loaded: throw, older
596 Older extension is tested with current version, the other only with newer:
687 Older extension is tested with current version, the other only with newer:
597 $ echo "util.version = lambda:'1.9.3'" >> older.py
688 $ echo "util.version = lambda:'1.9.3'" >> older.py
598 $ rm -f older.pyc older.pyo
689 $ rm -f older.pyc older.pyo
599 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
690 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
600 > throw 2>&1 | egrep '^\*\*'
691 > throw 2>&1 | egrep '^\*\*'
601 ** Unknown exception encountered with possibly-broken third-party extension throw
692 ** Unknown exception encountered with possibly-broken third-party extension throw
602 ** which supports versions 2.1.1 of Mercurial.
693 ** which supports versions 2.1.1 of Mercurial.
603 ** Please disable throw and try your action again.
694 ** Please disable throw and try your action again.
604 ** If that fixes the bug please report it to http://example.com/bts
695 ** If that fixes the bug please report it to http://example.com/bts
605 ** Python * (glob)
696 ** Python * (glob)
606 ** Mercurial Distributed SCM (version 1.9.3)
697 ** Mercurial Distributed SCM (version 1.9.3)
607 ** Extensions loaded: throw, older
698 ** Extensions loaded: throw, older
608
699
609 Declare the version as supporting this hg version, show regular bts link:
700 Declare the version as supporting this hg version, show regular bts link:
610 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
701 $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'`
611 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
702 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
612 $ rm -f throw.pyc throw.pyo
703 $ rm -f throw.pyc throw.pyo
613 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
704 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
614 ** unknown exception encountered, please report by visiting
705 ** unknown exception encountered, please report by visiting
615 ** http://mercurial.selenic.com/wiki/BugTracker
706 ** http://mercurial.selenic.com/wiki/BugTracker
616 ** Python * (glob)
707 ** Python * (glob)
617 ** Mercurial Distributed SCM (*) (glob)
708 ** Mercurial Distributed SCM (*) (glob)
618 ** Extensions loaded: throw
709 ** Extensions loaded: throw
619
710
620 Restore HGRCPATH
711 Restore HGRCPATH
621
712
622 $ HGRCPATH=$ORGHGRCPATH
713 $ HGRCPATH=$ORGHGRCPATH
623 $ export HGRCPATH
714 $ export HGRCPATH
624
715
625 Commands handling multiple repositories at a time should invoke only
716 Commands handling multiple repositories at a time should invoke only
626 "reposetup()" of extensions enabling in the target repository.
717 "reposetup()" of extensions enabling in the target repository.
627
718
628 $ mkdir reposetup-test
719 $ mkdir reposetup-test
629 $ cd reposetup-test
720 $ cd reposetup-test
630
721
631 $ cat > $TESTTMP/reposetuptest.py <<EOF
722 $ cat > $TESTTMP/reposetuptest.py <<EOF
632 > from mercurial import extensions
723 > from mercurial import extensions
633 > def reposetup(ui, repo):
724 > def reposetup(ui, repo):
634 > ui.write('reposetup() for %s\n' % (repo.root))
725 > ui.write('reposetup() for %s\n' % (repo.root))
635 > EOF
726 > EOF
636 $ hg init src
727 $ hg init src
637 $ echo a > src/a
728 $ echo a > src/a
638 $ hg -R src commit -Am '#0 at src/a'
729 $ hg -R src commit -Am '#0 at src/a'
639 adding a
730 adding a
640 $ echo '[extensions]' >> src/.hg/hgrc
731 $ echo '[extensions]' >> src/.hg/hgrc
641 $ echo '# enable extension locally' >> src/.hg/hgrc
732 $ echo '# enable extension locally' >> src/.hg/hgrc
642 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
733 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
643 $ hg -R src status
734 $ hg -R src status
644 reposetup() for $TESTTMP/reposetup-test/src
735 reposetup() for $TESTTMP/reposetup-test/src
645
736
646 $ hg clone -U src clone-dst1
737 $ hg clone -U src clone-dst1
647 reposetup() for $TESTTMP/reposetup-test/src
738 reposetup() for $TESTTMP/reposetup-test/src
648 $ hg init push-dst1
739 $ hg init push-dst1
649 $ hg -q -R src push push-dst1
740 $ hg -q -R src push push-dst1
650 reposetup() for $TESTTMP/reposetup-test/src
741 reposetup() for $TESTTMP/reposetup-test/src
651 $ hg init pull-src1
742 $ hg init pull-src1
652 $ hg -q -R pull-src1 pull src
743 $ hg -q -R pull-src1 pull src
653 reposetup() for $TESTTMP/reposetup-test/src
744 reposetup() for $TESTTMP/reposetup-test/src
654
745
655 $ echo '[extensions]' >> $HGRCPATH
746 $ echo '[extensions]' >> $HGRCPATH
656 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
747 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
657 $ echo 'reposetuptest = !' >> $HGRCPATH
748 $ echo 'reposetuptest = !' >> $HGRCPATH
658 $ hg clone -U src clone-dst2
749 $ hg clone -U src clone-dst2
659 reposetup() for $TESTTMP/reposetup-test/src
750 reposetup() for $TESTTMP/reposetup-test/src
660 $ hg init push-dst2
751 $ hg init push-dst2
661 $ hg -q -R src push push-dst2
752 $ hg -q -R src push push-dst2
662 reposetup() for $TESTTMP/reposetup-test/src
753 reposetup() for $TESTTMP/reposetup-test/src
663 $ hg init pull-src2
754 $ hg init pull-src2
664 $ hg -q -R pull-src2 pull src
755 $ hg -q -R pull-src2 pull src
665 reposetup() for $TESTTMP/reposetup-test/src
756 reposetup() for $TESTTMP/reposetup-test/src
666
757
667 $ echo '[extensions]' >> $HGRCPATH
758 $ echo '[extensions]' >> $HGRCPATH
668 $ echo '# enable extension globally' >> $HGRCPATH
759 $ echo '# enable extension globally' >> $HGRCPATH
669 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> $HGRCPATH
760 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> $HGRCPATH
670 $ hg clone -U src clone-dst3
761 $ hg clone -U src clone-dst3
671 reposetup() for $TESTTMP/reposetup-test/src
762 reposetup() for $TESTTMP/reposetup-test/src
672 reposetup() for $TESTTMP/reposetup-test/clone-dst3
763 reposetup() for $TESTTMP/reposetup-test/clone-dst3
673 $ hg init push-dst3
764 $ hg init push-dst3
674 reposetup() for $TESTTMP/reposetup-test/push-dst3
765 reposetup() for $TESTTMP/reposetup-test/push-dst3
675 $ hg -q -R src push push-dst3
766 $ hg -q -R src push push-dst3
676 reposetup() for $TESTTMP/reposetup-test/src
767 reposetup() for $TESTTMP/reposetup-test/src
677 reposetup() for $TESTTMP/reposetup-test/push-dst3
768 reposetup() for $TESTTMP/reposetup-test/push-dst3
678 $ hg init pull-src3
769 $ hg init pull-src3
679 reposetup() for $TESTTMP/reposetup-test/pull-src3
770 reposetup() for $TESTTMP/reposetup-test/pull-src3
680 $ hg -q -R pull-src3 pull src
771 $ hg -q -R pull-src3 pull src
681 reposetup() for $TESTTMP/reposetup-test/pull-src3
772 reposetup() for $TESTTMP/reposetup-test/pull-src3
682 reposetup() for $TESTTMP/reposetup-test/src
773 reposetup() for $TESTTMP/reposetup-test/src
683
774
684 $ echo '[extensions]' >> src/.hg/hgrc
775 $ echo '[extensions]' >> src/.hg/hgrc
685 $ echo '# disable extension locally' >> src/.hg/hgrc
776 $ echo '# disable extension locally' >> src/.hg/hgrc
686 $ echo 'reposetuptest = !' >> src/.hg/hgrc
777 $ echo 'reposetuptest = !' >> src/.hg/hgrc
687 $ hg clone -U src clone-dst4
778 $ hg clone -U src clone-dst4
688 reposetup() for $TESTTMP/reposetup-test/clone-dst4
779 reposetup() for $TESTTMP/reposetup-test/clone-dst4
689 $ hg init push-dst4
780 $ hg init push-dst4
690 reposetup() for $TESTTMP/reposetup-test/push-dst4
781 reposetup() for $TESTTMP/reposetup-test/push-dst4
691 $ hg -q -R src push push-dst4
782 $ hg -q -R src push push-dst4
692 reposetup() for $TESTTMP/reposetup-test/push-dst4
783 reposetup() for $TESTTMP/reposetup-test/push-dst4
693 $ hg init pull-src4
784 $ hg init pull-src4
694 reposetup() for $TESTTMP/reposetup-test/pull-src4
785 reposetup() for $TESTTMP/reposetup-test/pull-src4
695 $ hg -q -R pull-src4 pull src
786 $ hg -q -R pull-src4 pull src
696 reposetup() for $TESTTMP/reposetup-test/pull-src4
787 reposetup() for $TESTTMP/reposetup-test/pull-src4
697
788
698 disabling in command line overlays with all configuration
789 disabling in command line overlays with all configuration
699 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
790 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
700 $ hg --config extensions.reposetuptest=! init push-dst5
791 $ hg --config extensions.reposetuptest=! init push-dst5
701 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
792 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
702 $ hg --config extensions.reposetuptest=! init pull-src5
793 $ hg --config extensions.reposetuptest=! init pull-src5
703 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
794 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
704
795
705 $ echo '[extensions]' >> $HGRCPATH
796 $ echo '[extensions]' >> $HGRCPATH
706 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
797 $ echo '# disable extension globally and explicitly' >> $HGRCPATH
707 $ echo 'reposetuptest = !' >> $HGRCPATH
798 $ echo 'reposetuptest = !' >> $HGRCPATH
708 $ hg init parent
799 $ hg init parent
709 $ hg init parent/sub1
800 $ hg init parent/sub1
710 $ echo 1 > parent/sub1/1
801 $ echo 1 > parent/sub1/1
711 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
802 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
712 adding 1
803 adding 1
713 $ hg init parent/sub2
804 $ hg init parent/sub2
714 $ hg init parent/sub2/sub21
805 $ hg init parent/sub2/sub21
715 $ echo 21 > parent/sub2/sub21/21
806 $ echo 21 > parent/sub2/sub21/21
716 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
807 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
717 adding 21
808 adding 21
718 $ cat > parent/sub2/.hgsub <<EOF
809 $ cat > parent/sub2/.hgsub <<EOF
719 > sub21 = sub21
810 > sub21 = sub21
720 > EOF
811 > EOF
721 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
812 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
722 adding .hgsub
813 adding .hgsub
723 $ hg init parent/sub3
814 $ hg init parent/sub3
724 $ echo 3 > parent/sub3/3
815 $ echo 3 > parent/sub3/3
725 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
816 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
726 adding 3
817 adding 3
727 $ cat > parent/.hgsub <<EOF
818 $ cat > parent/.hgsub <<EOF
728 > sub1 = sub1
819 > sub1 = sub1
729 > sub2 = sub2
820 > sub2 = sub2
730 > sub3 = sub3
821 > sub3 = sub3
731 > EOF
822 > EOF
732 $ hg -R parent commit -Am '#0 at parent'
823 $ hg -R parent commit -Am '#0 at parent'
733 adding .hgsub
824 adding .hgsub
734 $ echo '[extensions]' >> parent/.hg/hgrc
825 $ echo '[extensions]' >> parent/.hg/hgrc
735 $ echo '# enable extension locally' >> parent/.hg/hgrc
826 $ echo '# enable extension locally' >> parent/.hg/hgrc
736 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
827 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
737 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
828 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
738 $ hg -R parent status -S -A
829 $ hg -R parent status -S -A
739 reposetup() for $TESTTMP/reposetup-test/parent
830 reposetup() for $TESTTMP/reposetup-test/parent
740 reposetup() for $TESTTMP/reposetup-test/parent/sub2
831 reposetup() for $TESTTMP/reposetup-test/parent/sub2
741 C .hgsub
832 C .hgsub
742 C .hgsubstate
833 C .hgsubstate
743 C sub1/1
834 C sub1/1
744 C sub2/.hgsub
835 C sub2/.hgsub
745 C sub2/.hgsubstate
836 C sub2/.hgsubstate
746 C sub2/sub21/21
837 C sub2/sub21/21
747 C sub3/3
838 C sub3/3
748
839
749 $ cd ..
840 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now