68 mod = None |
68 mod = None |
69 try: |
69 try: |
70 mod = _extensions[name] |
70 mod = _extensions[name] |
71 except KeyError: |
71 except KeyError: |
72 for k, v in _extensions.iteritems(): |
72 for k, v in _extensions.iteritems(): |
73 if k.endswith('.' + name) or k.endswith('/' + name): |
73 if k.endswith(b'.' + name) or k.endswith(b'/' + name): |
74 mod = v |
74 mod = v |
75 break |
75 break |
76 if not mod: |
76 if not mod: |
77 raise KeyError(name) |
77 raise KeyError(name) |
78 return mod |
78 return mod |
79 |
79 |
80 |
80 |
81 def loadpath(path, module_name): |
81 def loadpath(path, module_name): |
82 module_name = module_name.replace('.', '_') |
82 module_name = module_name.replace(b'.', b'_') |
83 path = util.normpath(util.expandpath(path)) |
83 path = util.normpath(util.expandpath(path)) |
84 module_name = pycompat.fsdecode(module_name) |
84 module_name = pycompat.fsdecode(module_name) |
85 path = pycompat.fsdecode(path) |
85 path = pycompat.fsdecode(path) |
86 if os.path.isdir(path): |
86 if os.path.isdir(path): |
87 # module/__init__.py style |
87 # module/__init__.py style |
98 |
98 |
99 |
99 |
100 def _importh(name): |
100 def _importh(name): |
101 """import and return the <name> module""" |
101 """import and return the <name> module""" |
102 mod = __import__(pycompat.sysstr(name)) |
102 mod = __import__(pycompat.sysstr(name)) |
103 components = name.split('.') |
103 components = name.split(b'.') |
104 for comp in components[1:]: |
104 for comp in components[1:]: |
105 mod = getattr(mod, comp) |
105 mod = getattr(mod, comp) |
106 return mod |
106 return mod |
107 |
107 |
108 |
108 |
109 def _importext(name, path=None, reportfunc=None): |
109 def _importext(name, path=None, reportfunc=None): |
110 if path: |
110 if path: |
111 # the module will be loaded in sys.modules |
111 # the module will be loaded in sys.modules |
112 # choose an unique name so that it doesn't |
112 # choose an unique name so that it doesn't |
113 # conflicts with other modules |
113 # conflicts with other modules |
114 mod = loadpath(path, 'hgext.%s' % name) |
114 mod = loadpath(path, b'hgext.%s' % name) |
115 else: |
115 else: |
116 try: |
116 try: |
117 mod = _importh("hgext.%s" % name) |
117 mod = _importh(b"hgext.%s" % name) |
118 except ImportError as err: |
118 except ImportError as err: |
119 if reportfunc: |
119 if reportfunc: |
120 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name) |
120 reportfunc(err, b"hgext.%s" % name, b"hgext3rd.%s" % name) |
121 try: |
121 try: |
122 mod = _importh("hgext3rd.%s" % name) |
122 mod = _importh(b"hgext3rd.%s" % name) |
123 except ImportError as err: |
123 except ImportError as err: |
124 if reportfunc: |
124 if reportfunc: |
125 reportfunc(err, "hgext3rd.%s" % name, name) |
125 reportfunc(err, b"hgext3rd.%s" % name, name) |
126 mod = _importh(name) |
126 mod = _importh(name) |
127 return mod |
127 return mod |
128 |
128 |
129 |
129 |
130 def _reportimporterror(ui, err, failed, next): |
130 def _reportimporterror(ui, err, failed, next): |
150 _rejectunicode(name, k) |
150 _rejectunicode(name, k) |
151 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v) |
151 _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v) |
152 elif isinstance(xs, type(u'')): |
152 elif isinstance(xs, type(u'')): |
153 raise error.ProgrammingError( |
153 raise error.ProgrammingError( |
154 b"unicode %r found in %s" % (xs, name), |
154 b"unicode %r found in %s" % (xs, name), |
155 hint="use b'' to make it byte string", |
155 hint=b"use b'' to make it byte string", |
156 ) |
156 ) |
157 |
157 |
158 |
158 |
159 # attributes set by registrar.command |
159 # attributes set by registrar.command |
160 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo') |
160 _cmdfuncattrs = (b'norepo', b'optionalrepo', b'inferrepo') |
161 |
161 |
162 |
162 |
163 def _validatecmdtable(ui, cmdtable): |
163 def _validatecmdtable(ui, cmdtable): |
164 """Check if extension commands have required attributes""" |
164 """Check if extension commands have required attributes""" |
165 for c, e in cmdtable.iteritems(): |
165 for c, e in cmdtable.iteritems(): |
166 f = e[0] |
166 f = e[0] |
167 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)] |
167 missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)] |
168 if not missing: |
168 if not missing: |
169 continue |
169 continue |
170 raise error.ProgrammingError( |
170 raise error.ProgrammingError( |
171 'missing attributes: %s' % ', '.join(missing), |
171 b'missing attributes: %s' % b', '.join(missing), |
172 hint="use @command decorator to register '%s'" % c, |
172 hint=b"use @command decorator to register '%s'" % c, |
173 ) |
173 ) |
174 |
174 |
175 |
175 |
176 def _validatetables(ui, mod): |
176 def _validatetables(ui, mod): |
177 """Sanity check for loadable tables provided by extension module""" |
177 """Sanity check for loadable tables provided by extension module""" |
178 for t in ['cmdtable', 'colortable', 'configtable']: |
178 for t in [b'cmdtable', b'colortable', b'configtable']: |
179 _rejectunicode(t, getattr(mod, t, {})) |
179 _rejectunicode(t, getattr(mod, t, {})) |
180 for t in [ |
180 for t in [ |
181 'filesetpredicate', |
181 b'filesetpredicate', |
182 'internalmerge', |
182 b'internalmerge', |
183 'revsetpredicate', |
183 b'revsetpredicate', |
184 'templatefilter', |
184 b'templatefilter', |
185 'templatefunc', |
185 b'templatefunc', |
186 'templatekeyword', |
186 b'templatekeyword', |
187 ]: |
187 ]: |
188 o = getattr(mod, t, None) |
188 o = getattr(mod, t, None) |
189 if o: |
189 if o: |
190 _rejectunicode(t, o._table) |
190 _rejectunicode(t, o._table) |
191 _validatecmdtable(ui, getattr(mod, 'cmdtable', {})) |
191 _validatecmdtable(ui, getattr(mod, 'cmdtable', {})) |
192 |
192 |
193 |
193 |
194 def load(ui, name, path, loadingtime=None): |
194 def load(ui, name, path, loadingtime=None): |
195 if name.startswith('hgext.') or name.startswith('hgext/'): |
195 if name.startswith(b'hgext.') or name.startswith(b'hgext/'): |
196 shortname = name[6:] |
196 shortname = name[6:] |
197 else: |
197 else: |
198 shortname = name |
198 shortname = name |
199 if shortname in _builtin: |
199 if shortname in _builtin: |
200 return None |
200 return None |
201 if shortname in _extensions: |
201 if shortname in _extensions: |
202 return _extensions[shortname] |
202 return _extensions[shortname] |
203 ui.log(b'extension', b' - loading extension: %s\n', shortname) |
203 ui.log(b'extension', b' - loading extension: %s\n', shortname) |
204 _extensions[shortname] = None |
204 _extensions[shortname] = None |
205 with util.timedcm('load extension %s', shortname) as stats: |
205 with util.timedcm(b'load extension %s', shortname) as stats: |
206 mod = _importext(name, path, bind(_reportimporterror, ui)) |
206 mod = _importext(name, path, bind(_reportimporterror, ui)) |
207 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats) |
207 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats) |
208 if loadingtime is not None: |
208 if loadingtime is not None: |
209 loadingtime[shortname] += stats.elapsed |
209 loadingtime[shortname] += stats.elapsed |
210 |
210 |
213 # extensions short circuit when loaded with a known incompatible version |
213 # extensions short circuit when loaded with a known incompatible version |
214 # of Mercurial. |
214 # of Mercurial. |
215 minver = getattr(mod, 'minimumhgversion', None) |
215 minver = getattr(mod, 'minimumhgversion', None) |
216 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2): |
216 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2): |
217 msg = _( |
217 msg = _( |
218 '(third party extension %s requires version %s or newer ' |
218 b'(third party extension %s requires version %s or newer ' |
219 'of Mercurial (current: %s); disabling)\n' |
219 b'of Mercurial (current: %s); disabling)\n' |
220 ) |
220 ) |
221 ui.warn(msg % (shortname, minver, util.version())) |
221 ui.warn(msg % (shortname, minver, util.version())) |
222 return |
222 return |
223 ui.log(b'extension', b' - validating extension tables: %s\n', shortname) |
223 ui.log(b'extension', b' - validating extension tables: %s\n', shortname) |
224 _validatetables(ui, mod) |
224 _validatetables(ui, mod) |
254 try: |
254 try: |
255 extsetup(ui) |
255 extsetup(ui) |
256 except Exception as inst: |
256 except Exception as inst: |
257 ui.traceback(force=True) |
257 ui.traceback(force=True) |
258 msg = stringutil.forcebytestr(inst) |
258 msg = stringutil.forcebytestr(inst) |
259 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg)) |
259 ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg)) |
260 return False |
260 return False |
261 return True |
261 return True |
262 |
262 |
263 |
263 |
264 def loadall(ui, whitelist=None): |
264 def loadall(ui, whitelist=None): |
265 loadingtime = collections.defaultdict(int) |
265 loadingtime = collections.defaultdict(int) |
266 result = ui.configitems("extensions") |
266 result = ui.configitems(b"extensions") |
267 if whitelist is not None: |
267 if whitelist is not None: |
268 result = [(k, v) for (k, v) in result if k in whitelist] |
268 result = [(k, v) for (k, v) in result if k in whitelist] |
269 newindex = len(_order) |
269 newindex = len(_order) |
270 ui.log( |
270 ui.log( |
271 b'extension', |
271 b'extension', |
272 b'loading %sextensions\n', |
272 b'loading %sextensions\n', |
273 'additional ' if newindex else '', |
273 b'additional ' if newindex else b'', |
274 ) |
274 ) |
275 ui.log(b'extension', b'- processing %d entries\n', len(result)) |
275 ui.log(b'extension', b'- processing %d entries\n', len(result)) |
276 with util.timedcm('load all extensions') as stats: |
276 with util.timedcm(b'load all extensions') as stats: |
277 for (name, path) in result: |
277 for (name, path) in result: |
278 if path: |
278 if path: |
279 if path[0:1] == '!': |
279 if path[0:1] == b'!': |
280 if name not in _disabledextensions: |
280 if name not in _disabledextensions: |
281 ui.log( |
281 ui.log( |
282 b'extension', |
282 b'extension', |
283 b' - skipping disabled extension: %s\n', |
283 b' - skipping disabled extension: %s\n', |
284 name, |
284 name, |
289 load(ui, name, path, loadingtime) |
289 load(ui, name, path, loadingtime) |
290 except Exception as inst: |
290 except Exception as inst: |
291 msg = stringutil.forcebytestr(inst) |
291 msg = stringutil.forcebytestr(inst) |
292 if path: |
292 if path: |
293 ui.warn( |
293 ui.warn( |
294 _("*** failed to import extension %s from %s: %s\n") |
294 _(b"*** failed to import extension %s from %s: %s\n") |
295 % (name, path, msg) |
295 % (name, path, msg) |
296 ) |
296 ) |
297 else: |
297 else: |
298 ui.warn( |
298 ui.warn( |
299 _("*** failed to import extension %s: %s\n") |
299 _(b"*** failed to import extension %s: %s\n") |
300 % (name, msg) |
300 % (name, msg) |
301 ) |
301 ) |
302 if isinstance(inst, error.Hint) and inst.hint: |
302 if isinstance(inst, error.Hint) and inst.hint: |
303 ui.warn(_("*** (%s)\n") % inst.hint) |
303 ui.warn(_(b"*** (%s)\n") % inst.hint) |
304 ui.traceback() |
304 ui.traceback() |
305 |
305 |
306 ui.log( |
306 ui.log( |
307 b'extension', |
307 b'extension', |
308 b'> loaded %d extensions, total time %s\n', |
308 b'> loaded %d extensions, total time %s\n', |
316 # - loadername is the name of the function, |
316 # - loadername is the name of the function, |
317 # which takes (ui, extensionname, extraobj) arguments |
317 # which takes (ui, extensionname, extraobj) arguments |
318 # |
318 # |
319 # This one is for the list of item that must be run before running any setup |
319 # This one is for the list of item that must be run before running any setup |
320 earlyextraloaders = [ |
320 earlyextraloaders = [ |
321 ('configtable', configitems, 'loadconfigtable'), |
321 (b'configtable', configitems, b'loadconfigtable'), |
322 ] |
322 ] |
323 |
323 |
324 ui.log(b'extension', b'- loading configtable attributes\n') |
324 ui.log(b'extension', b'- loading configtable attributes\n') |
325 _loadextra(ui, newindex, earlyextraloaders) |
325 _loadextra(ui, newindex, earlyextraloaders) |
326 |
326 |
327 broken = set() |
327 broken = set() |
328 ui.log(b'extension', b'- executing uisetup hooks\n') |
328 ui.log(b'extension', b'- executing uisetup hooks\n') |
329 with util.timedcm('all uisetup') as alluisetupstats: |
329 with util.timedcm(b'all uisetup') as alluisetupstats: |
330 for name in _order[newindex:]: |
330 for name in _order[newindex:]: |
331 ui.log(b'extension', b' - running uisetup for %s\n', name) |
331 ui.log(b'extension', b' - running uisetup for %s\n', name) |
332 with util.timedcm('uisetup %s', name) as stats: |
332 with util.timedcm(b'uisetup %s', name) as stats: |
333 if not _runuisetup(name, ui): |
333 if not _runuisetup(name, ui): |
334 ui.log( |
334 ui.log( |
335 b'extension', |
335 b'extension', |
336 b' - the %s extension uisetup failed\n', |
336 b' - the %s extension uisetup failed\n', |
337 name, |
337 name, |
340 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats) |
340 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats) |
341 loadingtime[name] += stats.elapsed |
341 loadingtime[name] += stats.elapsed |
342 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats) |
342 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats) |
343 |
343 |
344 ui.log(b'extension', b'- executing extsetup hooks\n') |
344 ui.log(b'extension', b'- executing extsetup hooks\n') |
345 with util.timedcm('all extsetup') as allextetupstats: |
345 with util.timedcm(b'all extsetup') as allextetupstats: |
346 for name in _order[newindex:]: |
346 for name in _order[newindex:]: |
347 if name in broken: |
347 if name in broken: |
348 continue |
348 continue |
349 ui.log(b'extension', b' - running extsetup for %s\n', name) |
349 ui.log(b'extension', b' - running extsetup for %s\n', name) |
350 with util.timedcm('extsetup %s', name) as stats: |
350 with util.timedcm(b'extsetup %s', name) as stats: |
351 if not _runextsetup(name, ui): |
351 if not _runextsetup(name, ui): |
352 ui.log( |
352 ui.log( |
353 b'extension', |
353 b'extension', |
354 b' - the %s extension extsetup failed\n', |
354 b' - the %s extension extsetup failed\n', |
355 name, |
355 name, |
401 # - loadermod is the module where loader is placed |
401 # - loadermod is the module where loader is placed |
402 # - loadername is the name of the function, |
402 # - loadername is the name of the function, |
403 # which takes (ui, extensionname, extraobj) arguments |
403 # which takes (ui, extensionname, extraobj) arguments |
404 ui.log(b'extension', b'- loading extension registration objects\n') |
404 ui.log(b'extension', b'- loading extension registration objects\n') |
405 extraloaders = [ |
405 extraloaders = [ |
406 ('cmdtable', commands, 'loadcmdtable'), |
406 (b'cmdtable', commands, b'loadcmdtable'), |
407 ('colortable', color, 'loadcolortable'), |
407 (b'colortable', color, b'loadcolortable'), |
408 ('filesetpredicate', fileset, 'loadpredicate'), |
408 (b'filesetpredicate', fileset, b'loadpredicate'), |
409 ('internalmerge', filemerge, 'loadinternalmerge'), |
409 (b'internalmerge', filemerge, b'loadinternalmerge'), |
410 ('revsetpredicate', revset, 'loadpredicate'), |
410 (b'revsetpredicate', revset, b'loadpredicate'), |
411 ('templatefilter', templatefilters, 'loadfilter'), |
411 (b'templatefilter', templatefilters, b'loadfilter'), |
412 ('templatefunc', templatefuncs, 'loadfunction'), |
412 (b'templatefunc', templatefuncs, b'loadfunction'), |
413 ('templatekeyword', templatekw, 'loadkeyword'), |
413 (b'templatekeyword', templatekw, b'loadkeyword'), |
414 ] |
414 ] |
415 with util.timedcm('load registration objects') as stats: |
415 with util.timedcm(b'load registration objects') as stats: |
416 _loadextra(ui, newindex, extraloaders) |
416 _loadextra(ui, newindex, extraloaders) |
417 ui.log( |
417 ui.log( |
418 b'extension', |
418 b'extension', |
419 b'> extension registration object loading took %s\n', |
419 b'> extension registration object loading took %s\n', |
420 stats, |
420 stats, |
707 except OSError: |
707 except OSError: |
708 return {} |
708 return {} |
709 |
709 |
710 exts = {} |
710 exts = {} |
711 for e in files: |
711 for e in files: |
712 if e.endswith('.py'): |
712 if e.endswith(b'.py'): |
713 name = e.rsplit('.', 1)[0] |
713 name = e.rsplit(b'.', 1)[0] |
714 path = os.path.join(extpath, e) |
714 path = os.path.join(extpath, e) |
715 else: |
715 else: |
716 name = e |
716 name = e |
717 path = os.path.join(extpath, e, '__init__.py') |
717 path = os.path.join(extpath, e, b'__init__.py') |
718 if not os.path.exists(path): |
718 if not os.path.exists(path): |
719 continue |
719 continue |
720 if name in exts or name in _order or name == '__init__': |
720 if name in exts or name in _order or name == b'__init__': |
721 continue |
721 continue |
722 exts[name] = path |
722 exts[name] = path |
723 for name, path in _disabledextensions.iteritems(): |
723 for name, path in _disabledextensions.iteritems(): |
724 # If no path was provided for a disabled extension (e.g. "color=!"), |
724 # If no path was provided for a disabled extension (e.g. "color=!"), |
725 # don't replace the path we already found by the scan above. |
725 # don't replace the path we already found by the scan above. |
919 return [name for name, mod in _extensions.iteritems() if mod is None] |
919 return [name for name, mod in _extensions.iteritems() if mod is None] |
920 |
920 |
921 |
921 |
922 def moduleversion(module): |
922 def moduleversion(module): |
923 '''return version information from given module as a string''' |
923 '''return version information from given module as a string''' |
924 if util.safehasattr(module, 'getversion') and callable(module.getversion): |
924 if util.safehasattr(module, b'getversion') and callable(module.getversion): |
925 version = module.getversion() |
925 version = module.getversion() |
926 elif util.safehasattr(module, '__version__'): |
926 elif util.safehasattr(module, b'__version__'): |
927 version = module.__version__ |
927 version = module.__version__ |
928 else: |
928 else: |
929 version = '' |
929 version = b'' |
930 if isinstance(version, (list, tuple)): |
930 if isinstance(version, (list, tuple)): |
931 version = '.'.join(pycompat.bytestr(o) for o in version) |
931 version = b'.'.join(pycompat.bytestr(o) for o in version) |
932 return version |
932 return version |
933 |
933 |
934 |
934 |
935 def ismoduleinternal(module): |
935 def ismoduleinternal(module): |
936 exttestedwith = getattr(module, 'testedwith', None) |
936 exttestedwith = getattr(module, 'testedwith', None) |
937 return exttestedwith == "ships-with-hg-core" |
937 return exttestedwith == b"ships-with-hg-core" |