From 85748101fcb16682dfd5641979c6e5f025e5a8a5 Mon Sep 17 00:00:00 2001 From: Ulibos <49131389+Ulibos@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:15:53 +0300 Subject: [PATCH 1/5] sphinx-build: -M flag documented. --- sphinx/cmd/build.py | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py index 02fd99a8ccc..e8041b7440f 100644 --- a/sphinx/cmd/build.py +++ b/sphinx/cmd/build.py @@ -9,10 +9,11 @@ import multiprocessing import os import pdb # NoQA: T100 +import re import sys import traceback from os import path -from typing import TYPE_CHECKING, Any, TextIO +from typing import TYPE_CHECKING, Any, TextIO, IO from docutils.utils import SystemMessage @@ -110,10 +111,41 @@ def jobs_argument(value: str) -> int: else: return jobs +class ParagraphFormatter(argparse.HelpFormatter): + """Wraps help text as a default formatter but keeps paragraps separated.""" + _paragraph_matcher = re.compile(r"\n\n+") + + def _fill_text(self, text: str, width: int, indent: str) -> str: + import textwrap + result: list[str] = [] + for p in self._paragraph_matcher.split(text): + p = self._whitespace_matcher.sub(' ', p).strip() + p = textwrap.fill(p, width, + initial_indent=indent, + subsequent_indent=indent) + result.append(p) + return '\n\n'.join(result) + +class ArgParser(argparse.ArgumentParser): + """Wraps standard ArgumentParser to add sphinx-specefic flags to help.""" + def print_help(self, file: IO[str] | None = None) -> None: + from gettext import gettext as _ + # we inject -M flag action to positionals before printing help + # so that there is no risk of side effects on actual execution + for action_group in self._action_groups: + if not action_group.title == _('positional arguments'): + continue + m_flag = argparse.Action(["-M"], "BUILDER", # ugly but works + help=__('please refer to usage and main help section')) + action_group._group_actions.insert(0, m_flag) + break + return super().print_help(file) def get_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - usage='%(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]', + parser = ArgParser( + usage=( '%(prog)s -M BUILDER SOURCEDIR OUTPUTDIR [OPTIONS]\n' + ' %(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]'), + formatter_class=ParagraphFormatter, epilog=__('For more information, visit .'), description=__(""" Generate documentation from source files. @@ -123,6 +155,13 @@ def get_parser() -> argparse.ArgumentParser: settings. The 'sphinx-quickstart' tool may be used to generate template files, including 'conf.py' +PLEASE NOTE that there are 2 signatures for 2 usage scenarios. -M flag is a +special flag that must always come first if used! If present, it alters the +way the tool works. It can be thought of as a "one-click" solution. It allows +to build several formats of docs into the same OUTPUTDIR without mixing them +up (it creates a dedicated directory for each builder). If more control over +paths is needed then -b flag is the way to go. + sphinx-build can create documentation in different formats. A format is selected by specifying the builder name on the command line; it defaults to HTML. Builders can also perform other tasks related to documentation From 2a3733246b1d5b5d9cc99384991242a7c233e200 Mon Sep 17 00:00:00 2001 From: Ulibos <49131389+Ulibos@users.noreply.github.com> Date: Thu, 25 Jul 2024 22:30:30 +0300 Subject: [PATCH 2/5] sphinx-make: help color changed, --no-color support. --- sphinx/cmd/make_mode.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py index 01929469cca..1423a213bf9 100644 --- a/sphinx/cmd/make_mode.py +++ b/sphinx/cmd/make_mode.py @@ -17,7 +17,7 @@ import sphinx from sphinx.cmd.build import build_main -from sphinx.util.console import blue, bold, color_terminal, nocolor +from sphinx.util.console import yellow, bold, color_terminal, nocolor from sphinx.util.osutil import rmtree if sys.version_info >= (3, 11): @@ -86,14 +86,14 @@ def build_clean(self) -> int: return 0 def build_help(self) -> None: - if not color_terminal(): + if not color_terminal() or '--no-color' in sys.argv or '-N' in sys.argv: nocolor() print(bold("Sphinx v%s" % sphinx.__display_version__)) - print("Please use `make %s' where %s is one of" % ((blue('target'),) * 2)) + print("Please use `make %s' where %s is one of" % ((yellow('target'),) * 2)) for osname, bname, description in BUILDERS: if not osname or os.name == osname: - print(f' {blue(bname.ljust(10))} {description}') + print(f' {yellow(bname.ljust(10))} {description}') def build_latexpdf(self) -> int: if self.run_generic_build('latex') > 0: From 01bdcf747865971866c224a3d0f58077518e3d21 Mon Sep 17 00:00:00 2001 From: Ulibos <49131389+Ulibos@users.noreply.github.com> Date: Thu, 25 Jul 2024 23:16:43 +0300 Subject: [PATCH 3/5] docs: more info about -M flag. --- doc/man/sphinx-build.rst | 6 +++++- doc/tutorial/getting-started.rst | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/man/sphinx-build.rst b/doc/man/sphinx-build.rst index a8a21f8ce93..0d49ea469b5 100644 --- a/doc/man/sphinx-build.rst +++ b/doc/man/sphinx-build.rst @@ -4,7 +4,8 @@ sphinx-build Synopsis -------- -**sphinx-build** [*options*] <*sourcedir*> <*outputdir*> [*filenames* ...] +| **sphinx-build** -M <*builder*> <*sourcedir*> <*outputdir*> [*options*] +| **sphinx-build** [*options*] <*sourcedir*> <*outputdir*> [*filenames* ...] Description ----------- @@ -12,6 +13,9 @@ Description :program:`sphinx-build` generates documentation from the files in ```` and places it in the ````. +Please note that there are 2 signatures for 2 usage scenarios. ``-M`` flag +invokes a :ref:`make mode `, otherwise it works in a `build mode`. + :program:`sphinx-build` looks for ``/conf.py`` for the configuration settings. :manpage:`sphinx-quickstart(1)` may be used to generate template files, including ``conf.py``. diff --git a/doc/tutorial/getting-started.rst b/doc/tutorial/getting-started.rst index 678f08db3e7..f8e5188218e 100644 --- a/doc/tutorial/getting-started.rst +++ b/doc/tutorial/getting-started.rst @@ -106,6 +106,14 @@ the documentation as HTML for the first time. To do that, run this command: (.venv) $ sphinx-build -M html docs/source/ docs/build/ +.. note:: + + Please note that ``-M`` flag is a `make-mode` flag. ``sphinx-build`` also has + a `build-mode` that provides more control over cache directories but has less + builders available and has a different signature. It is invoked by using a + ``-b`` flag. These flags are mutually exclusive. You can find out more in + :doc:`sphinx-build's manual `. + And finally, open ``docs/build/html/index.html`` in your browser. You should see something like this: From 4c23fe141b9d4d8e467c8a5c501574f3bfa02bfe Mon Sep 17 00:00:00 2001 From: Ulibos <49131389+Ulibos@users.noreply.github.com> Date: Fri, 26 Jul 2024 03:49:16 +0300 Subject: [PATCH 4/5] Fix linting errors for M-flag PR. --- doc/man/sphinx-build.rst | 2 +- doc/tutorial/getting-started.rst | 4 ++-- sphinx/cmd/build.py | 19 +++++++++++++------ sphinx/cmd/make_mode.py | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/doc/man/sphinx-build.rst b/doc/man/sphinx-build.rst index 0d49ea469b5..74a546b9175 100644 --- a/doc/man/sphinx-build.rst +++ b/doc/man/sphinx-build.rst @@ -14,7 +14,7 @@ Description ```` and places it in the ````. Please note that there are 2 signatures for 2 usage scenarios. ``-M`` flag -invokes a :ref:`make mode `, otherwise it works in a `build mode`. +invokes a :ref:`make mode `, otherwise it works in a *build mode*. :program:`sphinx-build` looks for ``/conf.py`` for the configuration settings. :manpage:`sphinx-quickstart(1)` may be used to generate template diff --git a/doc/tutorial/getting-started.rst b/doc/tutorial/getting-started.rst index f8e5188218e..3b0821eea16 100644 --- a/doc/tutorial/getting-started.rst +++ b/doc/tutorial/getting-started.rst @@ -108,8 +108,8 @@ the documentation as HTML for the first time. To do that, run this command: .. note:: - Please note that ``-M`` flag is a `make-mode` flag. ``sphinx-build`` also has - a `build-mode` that provides more control over cache directories but has less + Please note that ``-M`` flag is a *make-mode* flag. ``sphinx-build`` also has + a *build-mode* that provides more control over cache directories but has less builders available and has a different signature. It is invoked by using a ``-b`` flag. These flags are mutually exclusive. You can find out more in :doc:`sphinx-build's manual `. diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py index e8041b7440f..74e99b1229a 100644 --- a/sphinx/cmd/build.py +++ b/sphinx/cmd/build.py @@ -13,7 +13,7 @@ import sys import traceback from os import path -from typing import TYPE_CHECKING, Any, TextIO, IO +from typing import IO, TYPE_CHECKING, Any, TextIO from docutils.utils import SystemMessage @@ -111,8 +111,10 @@ def jobs_argument(value: str) -> int: else: return jobs + class ParagraphFormatter(argparse.HelpFormatter): """Wraps help text as a default formatter but keeps paragraps separated.""" + _paragraph_matcher = re.compile(r"\n\n+") def _fill_text(self, text: str, width: int, indent: str) -> str: @@ -126,25 +128,30 @@ def _fill_text(self, text: str, width: int, indent: str) -> str: result.append(p) return '\n\n'.join(result) + class ArgParser(argparse.ArgumentParser): """Wraps standard ArgumentParser to add sphinx-specefic flags to help.""" + def print_help(self, file: IO[str] | None = None) -> None: from gettext import gettext as _ # we inject -M flag action to positionals before printing help # so that there is no risk of side effects on actual execution for action_group in self._action_groups: - if not action_group.title == _('positional arguments'): + if action_group.title != _('positional arguments'): continue - m_flag = argparse.Action(["-M"], "BUILDER", # ugly but works - help=__('please refer to usage and main help section')) + m_flag = argparse.Action( + ["-M"], "BUILDER", # ugly but works + help=__('please refer to usage and main help section') + ) action_group._group_actions.insert(0, m_flag) break return super().print_help(file) + def get_parser() -> argparse.ArgumentParser: parser = ArgParser( - usage=( '%(prog)s -M BUILDER SOURCEDIR OUTPUTDIR [OPTIONS]\n' - ' %(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]'), + usage=('%(prog)s -M BUILDER SOURCEDIR OUTPUTDIR [OPTIONS]\n ' + '%(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]'), formatter_class=ParagraphFormatter, epilog=__('For more information, visit .'), description=__(""" diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py index 1423a213bf9..43cc67371a7 100644 --- a/sphinx/cmd/make_mode.py +++ b/sphinx/cmd/make_mode.py @@ -17,7 +17,7 @@ import sphinx from sphinx.cmd.build import build_main -from sphinx.util.console import yellow, bold, color_terminal, nocolor +from sphinx.util.console import bold, color_terminal, nocolor, yellow from sphinx.util.osutil import rmtree if sys.version_info >= (3, 11): From 780b4db4b08091ece121a6e6c7435d19cb18ff12 Mon Sep 17 00:00:00 2001 From: Ulibos <49131389+Ulibos@users.noreply.github.com> Date: Wed, 7 Aug 2024 03:24:07 +0300 Subject: [PATCH 5/5] refactor: implement suggestions from the PR. --- doc/man/sphinx-build.rst | 8 +++++--- doc/tutorial/getting-started.rst | 9 ++++----- sphinx/cmd/build.py | 28 +++++++++++++++++----------- sphinx/cmd/make_mode.py | 3 ++- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/doc/man/sphinx-build.rst b/doc/man/sphinx-build.rst index 74a546b9175..5a2ee2bc2e0 100644 --- a/doc/man/sphinx-build.rst +++ b/doc/man/sphinx-build.rst @@ -4,8 +4,8 @@ sphinx-build Synopsis -------- -| **sphinx-build** -M <*builder*> <*sourcedir*> <*outputdir*> [*options*] | **sphinx-build** [*options*] <*sourcedir*> <*outputdir*> [*filenames* ...] +| **sphinx-build** -M <*builder*> <*sourcedir*> <*outputdir*> [*options*] Description ----------- @@ -13,8 +13,10 @@ Description :program:`sphinx-build` generates documentation from the files in ```` and places it in the ````. -Please note that there are 2 signatures for 2 usage scenarios. ``-M`` flag -invokes a :ref:`make mode `, otherwise it works in a *build mode*. +The available modes are the *build-mode* via :option:`sphinx-build -b` and the +*make-mode* via :option:`sphinx-build -M`. Note that these modes have a +different signature. The *make-mode* always requires the ``-M`` argument to +come first and options to come last. :program:`sphinx-build` looks for ``/conf.py`` for the configuration settings. :manpage:`sphinx-quickstart(1)` may be used to generate template diff --git a/doc/tutorial/getting-started.rst b/doc/tutorial/getting-started.rst index 3b0821eea16..7b2e4518bdd 100644 --- a/doc/tutorial/getting-started.rst +++ b/doc/tutorial/getting-started.rst @@ -108,11 +108,10 @@ the documentation as HTML for the first time. To do that, run this command: .. note:: - Please note that ``-M`` flag is a *make-mode* flag. ``sphinx-build`` also has - a *build-mode* that provides more control over cache directories but has less - builders available and has a different signature. It is invoked by using a - ``-b`` flag. These flags are mutually exclusive. You can find out more in - :doc:`sphinx-build's manual `. + :option:`sphinx-build -M` uses the *make-mode* but :program:`sphinx-build` + also supports a *build-mode* via :option:`sphinx-build -b` to provide a finer + control over the generated artifacts. See the :ref:`sphinx-build's manual + ` for details. And finally, open ``docs/build/html/index.html`` in your browser. You should see something like this: diff --git a/sphinx/cmd/build.py b/sphinx/cmd/build.py index 74e99b1229a..086e310fe65 100644 --- a/sphinx/cmd/build.py +++ b/sphinx/cmd/build.py @@ -132,7 +132,7 @@ def _fill_text(self, text: str, width: int, indent: str) -> str: class ArgParser(argparse.ArgumentParser): """Wraps standard ArgumentParser to add sphinx-specefic flags to help.""" - def print_help(self, file: IO[str] | None = None) -> None: + def __inject_help_entry(self) -> argparse._ArgumentGroup: from gettext import gettext as _ # we inject -M flag action to positionals before printing help # so that there is no risk of side effects on actual execution @@ -144,14 +144,22 @@ def print_help(self, file: IO[str] | None = None) -> None: help=__('please refer to usage and main help section') ) action_group._group_actions.insert(0, m_flag) - break - return super().print_help(file) + return action_group + err = ( + "Couldn't find the argument group 'positional arguments' to inject " + "-M flag's help info into.") + raise TypeError(err) + + def print_help(self, file: IO[str] | None = None) -> None: + action_group = self.__inject_help_entry() + super().print_help(file) + del action_group._group_actions[0] def get_parser() -> argparse.ArgumentParser: parser = ArgParser( - usage=('%(prog)s -M BUILDER SOURCEDIR OUTPUTDIR [OPTIONS]\n ' - '%(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]'), + usage=('%(prog)s [OPTIONS] SOURCEDIR OUTPUTDIR [FILENAMES...]\n ' + '%(prog)s -M BUILDER SOURCEDIR OUTPUTDIR [OPTIONS]'), formatter_class=ParagraphFormatter, epilog=__('For more information, visit .'), description=__(""" @@ -162,12 +170,10 @@ def get_parser() -> argparse.ArgumentParser: settings. The 'sphinx-quickstart' tool may be used to generate template files, including 'conf.py' -PLEASE NOTE that there are 2 signatures for 2 usage scenarios. -M flag is a -special flag that must always come first if used! If present, it alters the -way the tool works. It can be thought of as a "one-click" solution. It allows -to build several formats of docs into the same OUTPUTDIR without mixing them -up (it creates a dedicated directory for each builder). If more control over -paths is needed then -b flag is the way to go. +Use the '-M' option to simultaneously build several formats into the same +OUTPUTDIR. When specified, this option MUST be the first command-line argument. +If a finer control over the output is needed, use the '-b/--builder' option +instead. Note that these modes have a different signature. sphinx-build can create documentation in different formats. A format is selected by specifying the builder name on the command line; it defaults to diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py index 43cc67371a7..0692168d9ed 100644 --- a/sphinx/cmd/make_mode.py +++ b/sphinx/cmd/make_mode.py @@ -86,7 +86,8 @@ def build_clean(self) -> int: return 0 def build_help(self) -> None: - if not color_terminal() or '--no-color' in sys.argv or '-N' in sys.argv: + print(self.opts) + if not color_terminal() or '--no-color' in self.opts or '-N' in self.opts: nocolor() print(bold("Sphinx v%s" % sphinx.__display_version__))