Patchwork [WIP] help: show help content for disabled extension

login
register
mail settings
Submitter liscju
Date Oct. 8, 2016, 3:22 p.m.
Message ID <5497a92be82db414f691.1475940146@liscju-VirtualBox>
Download mbox | patch
Permalink /patch/16936/
State Changes Requested
Headers show

Comments

liscju - Oct. 8, 2016, 3:22 p.m.
# HG changeset patch
# User liscju <piotr.listkiewicz@gmail.com>
# Date 1475926049 -7200
#      Sat Oct 08 13:27:29 2016 +0200
# Node ID 5497a92be82db414f6913f869d5acd18b205cb2b
# Parent  91a3c58ecf938ed675f5364b88f0d663f12b0047
help: show help content for disabled extension

So far for disabled extension help showed only extension
summary. This commit makes help read content of the help
comments in extension module without actually loading
the module.
Yuya Nishihara - Oct. 9, 2016, 7:04 a.m.
On Sat, 08 Oct 2016 17:22:26 +0200, liscju wrote:
> # HG changeset patch
> # User liscju <piotr.listkiewicz@gmail.com>
> # Date 1475926049 -7200
> #      Sat Oct 08 13:27:29 2016 +0200
> # Node ID 5497a92be82db414f6913f869d5acd18b205cb2b
> # Parent  91a3c58ecf938ed675f5364b88f0d663f12b0047
> help: show help content for disabled extension
> 
> So far for disabled extension help showed only extension
> summary. This commit makes help read content of the help
> comments in extension module without actually loading
> the module.
> 
> diff --git a/mercurial/help.py b/mercurial/help.py
> --- a/mercurial/help.py
> +++ b/mercurial/help.py
> @@ -7,6 +7,7 @@
>  
>  from __future__ import absolute_import
>  
> +import ast
>  import itertools
>  import os
>  import textwrap
> @@ -300,6 +301,38 @@ addtopicsymbols('templates', '.. functio
>  addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
>                  dedent=True)
>  
> +def parsedisabledext(modname):

Have you tried the simpler way you pointed out? I said we could use ast
since I didn't know that.

https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-September/087972.html

> +    # return list of (command_name, command_module, command_doc)
> +    def finddisabledext(name):
> +        paths = extensions._disabledpaths()
> +        if name in paths:
> +            return paths[name]
> +        return None
> +
> +    def iscmd(funast):
> +        for decorator in funast.decorator_list:
> +            if isinstance(decorator, ast.Call) and decorator.func.id == 'command':
> +                return True
> +        return False
> +
> +    def extractcmds(extast):
> +        return [funast.name for funast in extast.body if isinstance(funast, ast.FunctionDef) and iscmd(funast)]
> +
> +    def extractcmddoc(extast, cmd):
> +        funasts = [funast for funast in extast.body
> +                  if isinstance(funast, ast.FunctionDef) and iscmd(funast) and funast.name == cmd]
> +        if len(funasts) > 0:
> +            return ast.get_docstring(funasts[0])
> +        else:
> +            return None
> +
> +    extfile = open(finddisabledext(modname), 'r')
> +    try:
> +        extast = ast.parse(extfile.read())
> +        return [(cmdname, modname, extractcmddoc(extast, cmdname)) for cmdname in extractcmds(extast)]
> +    except SyntaxError:
> +        return []

Though we could provide the list of commands possibly defined by the disabled
extension, I don't think that's necessary because that would complicate things.
timeless - Oct. 21, 2016, 1:13 p.m.
Have we considered supporting `hg help churn.churn` where the first
churn is a (possibly) disabled extension name, and the second is the
name of a command within that extension?

I'm not saying we should do this right away, but I can see some
advantages to being able to support it later...
Yuya Nishihara - Oct. 22, 2016, 8:08 a.m.
On Fri, 21 Oct 2016 09:13:11 -0400, timeless wrote:
> Have we considered supporting `hg help churn.churn` where the first
> churn is a (possibly) disabled extension name, and the second is the
> name of a command within that extension?

Not yet?

> I'm not saying we should do this right away, but I can see some
> advantages to being able to support it later...

Perhaps that would be somewhat useful, but would be much complicated compared
to just parsing a module doc. That's why I think we should simply fix the
issue5228 first.

https://bz.mercurial-scm.org/show_bug.cgi?id=5228
Pulkit Goyal - Oct. 31, 2016, 6:14 p.m.
On Sat, Oct 22, 2016 at 1:38 PM, Yuya Nishihara <yuya@tcha.org> wrote:
> On Fri, 21 Oct 2016 09:13:11 -0400, timeless wrote:
>> Have we considered supporting `hg help churn.churn` where the first
>> churn is a (possibly) disabled extension name, and the second is the
>> name of a command within that extension?
>
> Not yet?

Well working on issue5228, I found that we support something like `hg
help qrecord` and
if record extension is not enabled it will say,

'qrecord' is provided by the following extension:

    record        commands to interactively select changes for commit/qrefresh
                  (DEPRECATED)
(use 'hg help extensions' for information on enabling extensions)

We support saying that this command is provided by the following
extension, if the
extension is disabled.

>> I'm not saying we should do this right away, but I can see some
>> advantages to being able to support it later...
>
> Perhaps that would be somewhat useful, but would be much complicated compared
> to just parsing a module doc. That's why I think we should simply fix the
> issue5228 first.
>
> https://bz.mercurial-scm.org/show_bug.cgi?id=5228
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Yuya Nishihara - Nov. 1, 2016, 12:32 p.m.
On Mon, 31 Oct 2016 23:44:27 +0530, Pulkit Goyal wrote:
> On Sat, Oct 22, 2016 at 1:38 PM, Yuya Nishihara <yuya@tcha.org> wrote:
> > On Fri, 21 Oct 2016 09:13:11 -0400, timeless wrote:
> >> Have we considered supporting `hg help churn.churn` where the first
> >> churn is a (possibly) disabled extension name, and the second is the
> >> name of a command within that extension?
> >
> > Not yet?
> 
> Well working on issue5228, I found that we support something like `hg
> help qrecord` and
> if record extension is not enabled it will say,
> 
> 'qrecord' is provided by the following extension:
> 
>     record        commands to interactively select changes for commit/qrefresh
>                   (DEPRECATED)
> (use 'hg help extensions' for information on enabling extensions)
> 
> We support saying that this command is provided by the following
> extension, if the
> extension is disabled.

Yep, we try importing all extension modules to provide this message. That's
why "hg unknown-command" is slow. I don't think we should reuse this hack
to provide the help of disabled extensions since that would mean disabled
modules could be loaded into long-running hgweb processes.

Patch

diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -7,6 +7,7 @@ 
 
 from __future__ import absolute_import
 
+import ast
 import itertools
 import os
 import textwrap
@@ -300,6 +301,38 @@  addtopicsymbols('templates', '.. functio
 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
                 dedent=True)
 
+def parsedisabledext(modname):
+    # return list of (command_name, command_module, command_doc)
+    def finddisabledext(name):
+        paths = extensions._disabledpaths()
+        if name in paths:
+            return paths[name]
+        return None
+
+    def iscmd(funast):
+        for decorator in funast.decorator_list:
+            if isinstance(decorator, ast.Call) and decorator.func.id == 'command':
+                return True
+        return False
+
+    def extractcmds(extast):
+        return [funast.name for funast in extast.body if isinstance(funast, ast.FunctionDef) and iscmd(funast)]
+
+    def extractcmddoc(extast, cmd):
+        funasts = [funast for funast in extast.body
+                  if isinstance(funast, ast.FunctionDef) and iscmd(funast) and funast.name == cmd]
+        if len(funasts) > 0:
+            return ast.get_docstring(funasts[0])
+        else:
+            return None
+
+    extfile = open(finddisabledext(modname), 'r')
+    try:
+        extast = ast.parse(extfile.read())
+        return [(cmdname, modname, extractcmddoc(extast, cmdname)) for cmdname in extractcmds(extast)]
+    except SyntaxError:
+        return []
+
 def help_(ui, name, unknowncmd=False, full=True, subtopic=None, **opts):
     '''
     Generate the help for 'name' as unformatted restructured text. If
@@ -395,7 +428,7 @@  def help_(ui, name, unknowncmd=False, fu
         return rst
 
 
-    def helplist(select=None, **opts):
+    def helplist(select=None, disabledext=None, **opts):
         # list of commands
         if name == "shortlist":
             header = _('basic commands:\n\n')
@@ -406,17 +439,24 @@  def help_(ui, name, unknowncmd=False, fu
 
         h = {}
         cmds = {}
-        for c, e in commands.table.iteritems():
+
+        if not disabledext:
+            commandinfos = [(c, e[0].__module__, e[0].__doc__) for c, e in commands.table.iteritems()]
+        else:
+            commandinfos = parsedisabledext(disabledext)
+
+        for cmdname, cmdmod, cmddoc in commandinfos:
+            c = cmdname
             f = c.partition("|")[0]
             if select and not select(f):
                 continue
             if (not select and name != 'shortlist' and
-                e[0].__module__ != commands.__name__):
+                cmdmod != commands.__name__):
                 continue
             if name == "shortlist" and not f.startswith("^"):
                 continue
             f = f.lstrip("^")
-            doc = e[0].__doc__
+            doc = cmddoc
             if filtercmd(ui, f, name, doc):
                 continue
             doc = gettext(doc)
@@ -548,7 +588,8 @@  def help_(ui, name, unknowncmd=False, fu
             modcmds = set([c.partition('|')[0] for c in ct])
             rst.extend(helplist(modcmds.__contains__))
         else:
-            rst.append(_("(use 'hg help extensions' for information on enabling"
+            rst.extend(helplist(lambda w: True, disabledext=name))
+            rst.append(_("\n(use 'hg help extensions' for information on enabling"
                        " extensions)\n"))
         return rst
 
diff --git a/tests/test-extension.t b/tests/test-extension.t
--- a/tests/test-extension.t
+++ b/tests/test-extension.t
@@ -1040,11 +1040,23 @@  Disabled extensions:
   $ hg help churn
   churn extension - command to display statistics about repository history
   
+  list of commands:
+  
+   churn         histogram of changes to the repository
+  
+  (use 'hg help -v -e churn' to show built-in aliases and global options)
+  
   (use 'hg help extensions' for information on enabling extensions)
 
   $ hg help patchbomb
   patchbomb extension - command to send changesets as (a series of) patch emails
   
+  list of commands:
+  
+   email         send changesets by email
+  
+  (use 'hg help -v patchbomb' to show built-in aliases and global options)
+  
   (use 'hg help extensions' for information on enabling extensions)
 
 
@@ -1065,6 +1077,8 @@  Broken disabled extension and command:
   $ hg --config extensions.path=./path.py help broken
   broken extension - (no help text available)
   
+  no commands defined
+  
   (use 'hg help extensions' for information on enabling extensions)
 
 
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -1636,6 +1636,8 @@  Show help content of disabled extensions
   $ hg help -e ambiguous
   ambiguous extension - (no help text available)
   
+  no commands defined
+  
   (use 'hg help extensions' for information on enabling extensions)
 
 Test dynamic list of merge tools only shows up once
diff --git a/tests/test-qrecord.t b/tests/test-qrecord.t
--- a/tests/test-qrecord.t
+++ b/tests/test-qrecord.t
@@ -9,6 +9,13 @@  help record (no record)
   record extension - commands to interactively select changes for
   commit/qrefresh (DEPRECATED)
   
+  list of commands:
+  
+   qrecord       interactively record a new patch
+   record        interactively select changes to commit
+  
+  (use 'hg help -v -e record' to show built-in aliases and global options)
+  
   (use 'hg help extensions' for information on enabling extensions)
 
 help qrecord (no record)