##// END OF EJS Templates
extensions: peek command table of disabled extensions without importing...
Yuya Nishihara -
r38180:bdf344ae default
parent child Browse files
Show More
@@ -7,6 +7,8
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ast
11 import collections
10 import functools
12 import functools
11 import imp
13 import imp
12 import inspect
14 import inspect
@@ -655,34 +657,67 def disabledext(name):
655 if name in paths:
657 if name in paths:
656 return _disabledhelp(paths[name])
658 return _disabledhelp(paths[name])
657
659
660 def _walkcommand(node):
661 """Scan @command() decorators in the tree starting at node"""
662 todo = collections.deque([node])
663 while todo:
664 node = todo.popleft()
665 if not isinstance(node, ast.FunctionDef):
666 todo.extend(ast.iter_child_nodes(node))
667 continue
668 for d in node.decorator_list:
669 if not isinstance(d, ast.Call):
670 continue
671 if not isinstance(d.func, ast.Name):
672 continue
673 if d.func.id != r'command':
674 continue
675 yield d
676
677 def _disabledcmdtable(path):
678 """Construct a dummy command table without loading the extension module
679
680 This may raise IOError or SyntaxError.
681 """
682 with open(path, 'rb') as src:
683 root = ast.parse(src.read(), path)
684 cmdtable = {}
685 for node in _walkcommand(root):
686 if not node.args:
687 continue
688 a = node.args[0]
689 if isinstance(a, ast.Str):
690 name = pycompat.sysbytes(a.s)
691 elif pycompat.ispy3 and isinstance(a, ast.Bytes):
692 name = a.s
693 else:
694 continue
695 cmdtable[name] = (None, [], b'')
696 return cmdtable
697
658 def _finddisabledcmd(ui, cmd, name, path, strict):
698 def _finddisabledcmd(ui, cmd, name, path, strict):
659 try:
699 try:
660 mod = loadpath(path, 'hgext.%s' % name)
700 cmdtable = _disabledcmdtable(path)
661 except Exception:
701 except (IOError, SyntaxError):
662 return
702 return
663 try:
703 try:
664 aliases, entry = cmdutil.findcmd(cmd,
704 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
665 getattr(mod, 'cmdtable', {}), strict)
666 except (error.AmbiguousCommand, error.UnknownCommand):
705 except (error.AmbiguousCommand, error.UnknownCommand):
667 return
706 return
668 except Exception:
669 ui.warn(_('warning: error finding commands in %s\n') % path)
670 ui.traceback()
671 return
672 for c in aliases:
707 for c in aliases:
673 if c.startswith(cmd):
708 if c.startswith(cmd):
674 cmd = c
709 cmd = c
675 break
710 break
676 else:
711 else:
677 cmd = aliases[0]
712 cmd = aliases[0]
678 doc = gettext(pycompat.getdoc(mod))
713 doc = _disabledhelp(path)
679 return (cmd, name, doc)
714 return (cmd, name, doc)
680
715
681 def disabledcmd(ui, cmd, strict=False):
716 def disabledcmd(ui, cmd, strict=False):
682 '''import disabled extensions until cmd is found.
717 '''find cmd from disabled extensions without importing.
683 returns (cmdname, extname, doc)'''
718 returns (cmdname, extname, doc)'''
684
719
685 paths = _disabledpaths(strip_init=True)
720 paths = _disabledpaths()
686 if not paths:
721 if not paths:
687 raise error.UnknownCommand(cmd)
722 raise error.UnknownCommand(cmd)
688
723
@@ -1229,9 +1229,14 Broken disabled extension and command:
1229
1229
1230 $ cat > hgext/forest.py <<EOF
1230 $ cat > hgext/forest.py <<EOF
1231 > cmdtable = None
1231 > cmdtable = None
1232 > @command()
1233 > def f():
1234 > pass
1235 > @command(123)
1236 > def g():
1237 > pass
1232 > EOF
1238 > EOF
1233 $ hg --config extensions.path=./path.py help foo > /dev/null
1239 $ hg --config extensions.path=./path.py help foo > /dev/null
1234 warning: error finding commands in $TESTTMP/hgext/forest.py
1235 abort: no such help topic: foo
1240 abort: no such help topic: foo
1236 (try 'hg help --keyword foo')
1241 (try 'hg help --keyword foo')
1237 [255]
1242 [255]
General Comments 0
You need to be logged in to leave comments. Login now