##// 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 b''
7 7
8 8 from __future__ import absolute_import
9 9
10 import ast
11 import collections
10 12 import functools
11 13 import imp
12 14 import inspect
@@ -655,34 +657,67 b' def disabledext(name):'
655 657 if name in paths:
656 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 698 def _finddisabledcmd(ui, cmd, name, path, strict):
659 699 try:
660 mod = loadpath(path, 'hgext.%s' % name)
661 except Exception:
700 cmdtable = _disabledcmdtable(path)
701 except (IOError, SyntaxError):
662 702 return
663 703 try:
664 aliases, entry = cmdutil.findcmd(cmd,
665 getattr(mod, 'cmdtable', {}), strict)
704 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
666 705 except (error.AmbiguousCommand, error.UnknownCommand):
667 706 return
668 except Exception:
669 ui.warn(_('warning: error finding commands in %s\n') % path)
670 ui.traceback()
671 return
672 707 for c in aliases:
673 708 if c.startswith(cmd):
674 709 cmd = c
675 710 break
676 711 else:
677 712 cmd = aliases[0]
678 doc = gettext(pycompat.getdoc(mod))
713 doc = _disabledhelp(path)
679 714 return (cmd, name, doc)
680 715
681 716 def disabledcmd(ui, cmd, strict=False):
682 '''import disabled extensions until cmd is found.
717 '''find cmd from disabled extensions without importing.
683 718 returns (cmdname, extname, doc)'''
684 719
685 paths = _disabledpaths(strip_init=True)
720 paths = _disabledpaths()
686 721 if not paths:
687 722 raise error.UnknownCommand(cmd)
688 723
@@ -1229,9 +1229,14 b' Broken disabled extension and command:'
1229 1229
1230 1230 $ cat > hgext/forest.py <<EOF
1231 1231 > cmdtable = None
1232 > @command()
1233 > def f():
1234 > pass
1235 > @command(123)
1236 > def g():
1237 > pass
1232 1238 > EOF
1233 1239 $ hg --config extensions.path=./path.py help foo > /dev/null
1234 warning: error finding commands in $TESTTMP/hgext/forest.py
1235 1240 abort: no such help topic: foo
1236 1241 (try 'hg help --keyword foo')
1237 1242 [255]
General Comments 0
You need to be logged in to leave comments. Login now