Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One manpage per subcommand output #45

Open
JOJ0 opened this issue Feb 22, 2021 · 7 comments
Open

One manpage per subcommand output #45

JOJ0 opened this issue Feb 22, 2021 · 7 comments

Comments

@JOJ0
Copy link

JOJ0 commented Feb 22, 2021

Is it possible to output multiple manpages/html pages eg for commands with multiple subcommands and a lot of options? I'd like to have the main command in a page and each subcommand in a separate page.

I try as follows but don't get the expected results.

Main command rst file - I try to remove all subcommands in the output my setting maxdepth: 0 but it does not work. Still all subcommands are included in the disco section:

.. autoprogram:: discodos.cmd.cli:ArgParse.parser
    :prog: disco
    :maxdepth: 0
    :groups:

one of the subcommand rst files - works as expected, only the disco searchcommand is shown:

.. autoprogram:: discodos.cmd.cli:ArgParse.parser
    :prog: disco
    :start_command: search
    :maxdepth: 1
    :groups

I have some index rst files too and in html output what I am trying to achieve looks quite good already, except the main command issue. These are the index files:

DiscoDOS manual
***************

.. toctree::
    :maxdepth: 2

    index_commands_reference
Commands reference
******************

.. toctree::
    :maxdepth: 1

    disco
    disco_search
    disco_mix
    disco_suggest
    disco_import
    disco_setup

In manpage output I still get one big manpage, containing all subcommands. How can I control that pages should be separated?

@JOJ0 JOJ0 changed the title single manpages one manpage per subcommand output Feb 22, 2021
@JOJ0 JOJ0 changed the title one manpage per subcommand output One manpage per subcommand output Feb 22, 2021
@JOJ0
Copy link
Author

JOJ0 commented Apr 30, 2021

Hi @langston-barrett , is there any chance you or any other maintainer will implement this? I am willing to help but would need some help. Tried to look into the code and it's a little over my head unfortunately. If someboday tells me where my starting point would be, that might help already.

So in short my feature idea is:

Rendering only the output of the main command but leave out all the subcommands.

My suggestion would be to use the already existing maxdepth argument. Setting it to 0 would leave out subcommands. I think this would be a self-explanatory thing but certainly I would include an addition to the autoprogram docs in my PR.

@langston-barrett
Copy link
Collaborator

Hi @langston-barrett , is there any chance you or any other maintainer will implement this?

I'm currently the only maintainer. I'm not personally interested in this feature, so I'm unlikely to implement it. However, I'm happy to review any PRs with this feature.

I try to remove all subcommands in the output my setting maxdepth: 0 but it does not work.

I bet you could fix this by making maxdepth into an Optional[int] here:

maxdepth: int = 0,

and changing the conditional here to be if maxdepth is not None and depth >= maxdepth:

if maxdepth and depth >= maxdepth:

You'd also need to change the line here that sets the default value:

maxdepth = int(self.options.get("maxdepth", 0))

Hope that helps!

@JOJ0
Copy link
Author

JOJ0 commented May 2, 2021

Hi @langston-barrett,
thanks a lot for your explanations, appreciated! But the thing is: Today I started to investigate this and realized that I funnily enough had debug statements around the lines you mentioned already in my code. I also realized that I back then found out that actually setting maxdepth to 1 (not 0) does the trick: It only renders the main command! This feature is actually already working and I am sorry that I bothered you with investigating in this direction!

BUT I also then recalled what the actual problem was back then: There is a nasty bug that prints the subcommands program name wrongly named as "sphinx-build". In the end it lands in a variable called title which I think is then used in the final rendered document.

So for example having these two rst files, one for the main command only, and for the subcommand only (it's a different program than in my first post but I think that doesn't matter, it's the same behaviour in both of them):

provision.rst

.. autoprogram:: lib_provision.cli:get_parser()
   :prog: provision
   :maxdepth: 1
   :no_usage_codeblock:

provision_install.rst

.. autoprogram:: lib_provision.cli:get_parser()
   :prog: provision
   :start_command: install
   :maxdepth: 1

Ok, so basically it works, Im am getting two html pages, one with the main command and one with only the subcommand. I put some debug statements in the code, let's see where it goes wrong with the program name:

(provision) jojo@gin ~/git/provision/docs (sphinx-ext-autopr) $ make clean; make html
Removing everything under 'build'...
Running Sphinx v1.8.5
making output directory...
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 3 source files that are out of date
updating environment: 3 added, 0 changed, 0 removed
parser in make_rst at the very start: ArgumentParser(prog='sphinx-build', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

prog in make_rst at the very start: provision

original_prog in make_rst at the very start: sphinx-build

parser.prog in make_rst at the very start: provision

inside 'if startcommand index 0 is empty string'

['']
cmd_parser in make_rst 'for commands..' loop: ArgumentParser(prog='provision', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

title in make_rst 'for commands..' loop: provision
maxdepth in scan_programs: 1

parser in make_rst at the very start: ArgumentParser(prog='provision', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

prog in make_rst at the very start: provision

original_prog in make_rst at the very start: provision

parser.prog in make_rst at the very start: provision

parser in make_rst 'if start_command': ArgumentParser(prog='sphinx-build install', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

prog in make_rst 'if start_command': provision

cmd_parser in make_rst 'for commands..' loop: ArgumentParser(prog='sphinx-build install', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

title in make_rst 'for commands..' loop: sphinx-build install
maxdepth in scan_programs: 1


looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] provision_install                                                  
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded.

The HTML pages are in build/html.
(provision) jojo@gin ~/git/provision/docs (sphinx-ext-autopr) $ 

I think you see what the problem is. I am unsure why this goes wrong. And also am unsure at which place it would be correct to fix it. Well, yes I could just quickfix it at the point where "title" variable is assigned by just replacing sphinx-build with the actual main program name but I think this wouldn't be appropriate and the fix should be somewhere much earlier in the code.

Also good to know is that this bug only occurs when as in my example autopgrogram is called twice / is in two rst files - one with main maxdepth 1, one with subcommand maxdepth 1 (or higher)

If I just render provision_install.rst - everything is fine the output looks like this:

(provision) jojo@gin ~/git/provision/docs (sphinx-ext-autopr) $ make clean; make html
Removing everything under 'build'...
Running Sphinx v1.8.5
making output directory...
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 2 source files that are out of date
updating environment: 2 added, 0 changed, 0 removed
parser in make_rst at the very start: ArgumentParser(prog='sphinx-build', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

prog in make_rst at the very start: provision

original_prog in make_rst at the very start: sphinx-build

parser.prog in make_rst at the very start: provision

parser in make_rst 'if start_command': ArgumentParser(prog='sphinx-build install', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

prog in make_rst 'if start_command': provision

cmd_parser in make_rst 'for commands..' loop: ArgumentParser(prog=u'provision install', usage=None, description=None, version=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

title in make_rst 'for commands..' loop: provision install
maxdepth in scan_programs: 1


looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] provision_install                                                  
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded.

The HTML pages are in build/html.
(provision) jojo@gin ~/git/provision/docs (sphinx-ext-autopr) $ 

@JOJ0
Copy link
Author

JOJ0 commented May 2, 2021

Alright, I think I am almost there. Added some more debug statements: around these lines:

if prog and parser.prog.startswith(original_prog):
parser.prog = parser.prog.replace(original_prog, prog, 1)

## both rst files: provision.rst, provision_install.rst

prog in make_rst 'if start_command': provision

parser.prog in make_rst 'if start_command': sphinx-build install

original_prog in make_rst 'if start_command': provision

prog in parser.prog was not replaced: sphinx-build install



## provision_install.rst file only

prog in make_rst 'if start_command': provision

parser.prog in make_rst 'if start_command': sphinx-build install

original_prog in make_rst 'if start_command': sphinx-build

prog in parser.prog was replaced: provision install

Ok, so it seems that in the first case, with both rst files, "original_prog" is set to provision and that's why the if condititon is not true and sphinx-build does not get replaced with the value of "prog"

@JOJ0
Copy link
Author

JOJ0 commented May 2, 2021

Also I think I am mixing up two issues here. Sorry for that but it just happened, so to clarify we are talking about these issues:

  • issue 1: rendering manpages always land in one big manpage (build/man/provision.1 in above's example), no matter whether
    • one rst file is used, containing the main command plus alls subcommands
    • several rst files are used, containing one main command/subcommand each
  • issue 2: original_prog is sphinx-build and is wrongly not replaced by prog name

I am investigating issue 2 at the moment. Note that issue 2 also applies to generating manpages even though my current examples do "make html"

Hope that helps tidying up any confusions :-)

@JOJ0
Copy link
Author

JOJ0 commented May 2, 2021

Hi @langston-barrett, do you agree that this could be fixed by replacing this:

if prog and cmd_parser.prog.startswith(original_prog):
cmd_parser.prog = cmd_parser.prog.replace(original_prog, prog, 1)

with something like that (excluding debug print statements)

                if prog:
                    print("inside for commands loop and 'if prog and startwith original'...\n")
                    cmd_parser.prog = cmd_parser.prog.replace(
                        cmd_parser.prog, prog, 1)
                    print("prog in parser.prog was replaced: {0}\n".format(cmd_parser.prog))
                else:
                    print("inside 'debug else branch of prog and startwith original'\n")

but probably I am too short-sighted here as I a don't understand why the if startswith was actually needed at all. If the user epxlicitly states :prog: in autoprogram directive than actually they always want prog to be replaced with what is stated in the directive, otherwise there wouldn't be any point of stating :prog:. Or am I missing something here?

If my assumption is true then I also don't understand why the ìf startswith` in this similar instance of that if statement a little further above in the code was necessary. Again here: If I state :prog: explicitly in the directive, I always want it to be replaced, right?

if prog and parser.prog.startswith(original_prog):
parser.prog = parser.prog.replace(original_prog, prog, 1)

@moux233
Copy link

moux233 commented Sep 23, 2022

Hi
I have the same bug: "sphinx-build" instead of program name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants