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 |
''' |
|
717 | '''find cmd from disabled extensions without importing. | |
683 | returns (cmdname, extname, doc)''' |
|
718 | returns (cmdname, extname, doc)''' | |
684 |
|
719 | |||
685 |
paths = _disabledpaths( |
|
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