Repository: VirtusLab/git-machete
Pull request #701 (2022-10-18 14:03:17)

Issue #695: Generate short docs out of `docs/source/cli.rst`


Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index cebb402..a37f23b 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,7 @@
 # Release notes
 
+## New in git-machete 3.12.6
+
 ## New in git-machete 3.12.5
 
 - added: `git machete github retarget-pr` now updates annotation for the branch associated with the retargeted PR
diff --git a/docs/enforce-docs-up-to-date.sh b/docs/enforce-docs-up-to-date.sh
index 4869016..967bb48 100755
--- a/docs/enforce-docs-up-to-date.sh
+++ b/docs/enforce-docs-up-to-date.sh
@@ -2,7 +2,7 @@
 
 set -e -o pipefail -u
 
-current_docs=$(<git_machete/generated_long_docs.py)
+current_docs=$(<git_machete/generated_docs.py)
 generated_docs=$(python docs/generate_py_docs.py)
 
 if [[ "$current_docs" != "$generated_docs" ]]; then
diff --git a/docs/generate_py_docs.py b/docs/generate_py_docs.py
index 884cec8..a62661d 100644
--- a/docs/generate_py_docs.py
+++ b/docs/generate_py_docs.py
@@ -19,9 +19,8 @@ def rst2html(input_string: str, source_path: str = None, destination_path: str =
     return parts['body']
 
 
-def html2txt(html: str):
-    html_elements = BeautifulSoup(html, features="html.parser")
-    INDENT = 3 * ' '
+def html2txt(html_: str):
+    html_elements = BeautifulSoup(html_, features="html.parser")
 
     # remove style tags
     for tag in html_elements.select('style'):
@@ -81,33 +80,33 @@ def html2txt(html: str):
                 lines = tag.text.splitlines()
                 tag.string = f' - {lines[0]}'
                 if len(lines) > 1:
-                    tag.string += indent('\n' + '\n'.join(lines[1:]), INDENT)
+                    tag.string += indent('\n' + '\n'.join(lines[1:]), INDENT_LEN_3)
 
     # add indent and bullet points to the bulleted list
     for tag in html_elements.select('li'):
         if tag.parent.parent is not None:
             if tag.parent.parent.name != 'li':
                 lines = tag.text.splitlines()
-                tag.string = f'\n{INDENT}* {lines[0]}'
+                tag.string = f'\n{INDENT_LEN_3}* {lines[0]}'
                 if len(lines) > 1:
-                    tag.string += indent('\n' + '\n'.join(lines[1:]), INDENT + '  ')
+                    tag.string += indent('\n' + '\n'.join(lines[1:]), INDENT_LEN_3 + '  ')
 
     # format elements in the `Option:` section by adding new line and indent
     for tag in html_elements.select('kbd'):
-        tag.insert_before(f'\n{INDENT}')
+        tag.insert_before(f'\n{INDENT_LEN_3}')
 
     # add indent to the description list
     for tag in html_elements.select('dt'):
-        tag.string = '\n' + indent(tag.text.strip(), INDENT)
+        tag.string = '\n' + indent(tag.text.strip(), INDENT_LEN_3)
 
     # add indent to the nested description list
     for tag in html_elements.select('dd'):
-        tag.string = '\n' + indent(tag.text, 2 * INDENT) + '\n'
+        tag.string = '\n' + indent(tag.text, 2 * INDENT_LEN_3) + '\n'
 
     # add new line and indent for the cells inside a table (e.g. options inside **Options:** section)
     for tag in html_elements.select('td'):
         if 'class' not in tag.attrs:
-            tag.string = indent(f'\n{tag.text}', 2 * INDENT)
+            tag.string = indent(f'\n{tag.text}', 2 * INDENT_LEN_3)
 
     # format code examples
     for tag in html_elements.select('pre'):
@@ -115,7 +114,7 @@ def html2txt(html: str):
             if ' '.join(tag.attrs['class']).replace('first', '').replace('last', '').replace('  ', ' ') == 'code literal-block':
                 tag.string = '\n<dim>' + indent(tag.text, '  ') + '</dim>'
             elif 'literal-block' in ' '.join(tag.attrs['class']):
-                tag.string = '<b>' + indent(tag.text.rstrip('\n'), INDENT) + '</b>'
+                tag.string = '<b>' + indent(tag.text.rstrip('\n'), INDENT_LEN_3) + '</b>'
 
     # build python docs string out of the previously formatted html tags
     text: str = ''
@@ -129,18 +128,18 @@ def html2txt(html: str):
     return text
 
 
-def skip_or_replace_unparseable_directives(rst: str) -> str:
-    rst = rst.replace(':ref:', '')
-    return rst
+def skip_or_replace_unparseable_directives(rst_: str) -> str:
+    rst_ = rst_.replace(':ref:', '')
+    return rst_
 
 
-def resolve_includes(rst: str, docs_source_path: str) -> str:
-    matches = re.findall(r'(.*)\.\. include:: (.*)', rst)
+def resolve_includes(rst_: str, docs_source_path_: str) -> str:
+    matches = re.findall(r'(.*)\.\. include:: (.*)', rst_)
     for indent_, match in matches:
-        with open(f'{docs_source_path}/{match}', 'r') as handle:
+        with open(f'{docs_source_path_}/{match}', 'r') as handle:
             include_text = handle.read()
-        rst = rst.replace(f'{indent_}.. include:: {match}', indent(dedent(include_text), indent_))
-    return rst
+        rst_ = rst_.replace(f'{indent_}.. include:: {match}', indent(dedent(include_text), indent_))
+    return rst_
 
 
 def skip_prefix_new_lines(txt: str) -> str:
@@ -155,12 +154,31 @@ def replace_3_newlines_and_more_with_2_newlines(txt: str) -> str:
 
 
 if __name__ == '__main__':
+    INDENT_LEN_3 = 3 * ' '
+    INDENT_LEN_4 = 4 * ' '
     docs_source_path = 'docs/source'
     warning_text = '# ---------------------------------------------------------------------------------------------------------\n' \
                    '# Warning: This file is NOT supposed to be edited directly, ' \
-                   'but instead regenerated via tox -e docs\n' \
+                   'but instead regenerated via `tox -e py-docs`\n' \
                    '# ---------------------------------------------------------------------------------------------------------\n'
-    output_text = 'from typing import Dict\n\n' + warning_text + '\nlong_docs: Dict[str, str] = {\n'
+    output_text = 'from typing import Dict\n\n' + warning_text
+
+    # build short docs
+    output_text += '\nshort_docs: Dict[str, str] = {\n'
+    short_docs_rst_file = docs_source_path + '/short_docs.rst'
+    with open(short_docs_rst_file, 'r') as f:
+        rst = f.read()
+    rst = skip_or_replace_unparseable_directives(rst)
+    html = rst2html(rst)
+    plain_text = html2txt(html)
+    plain_text = skip_prefix_new_lines(plain_text)
+    command_and_short_docs = re.findall(r'\* `([a-z-]*)`.*-- (.*)', plain_text)
+    for command, short_doc in command_and_short_docs:
+        output_text += f'{INDENT_LEN_4}"{command}": "{short_doc}",\n'
+    output_text += '}'
+
+    # build long docs
+    output_text += '\n\nlong_docs: Dict[str, str] = {\n'
     path = docs_source_path + '/cli_help'
     commands_and_file_paths = {f.split('.')[0]: join(path, f) for f in sorted(os.listdir(path)) if isfile(join(path, f))}
 
@@ -169,7 +187,7 @@ if __name__ == '__main__':
             rst = f.read()
 
         rst = skip_or_replace_unparseable_directives(rst)
-        rst = resolve_includes(rst=rst, docs_source_path=docs_source_path)
+        rst = resolve_includes(rst_=rst, docs_source_path_=docs_source_path)
 
         html = rst2html(rst)
 
@@ -177,7 +195,7 @@ if __name__ == '__main__':
         plain_text = skip_prefix_new_lines(plain_text)
         plain_text = plain_text.replace('---', '—')
 
-        output_text += f'    "{command}": """\n' + indent(plain_text, '        ') + '\n   """,\n'
+        output_text += f'{INDENT_LEN_4}"{command}": """\n' + indent(plain_text, 2 * INDENT_LEN_4) + '\n   """,\n'
         output_text = replace_3_newlines_and_more_with_2_newlines(output_text)
 
     output_text += '}'
diff --git a/docs/source/cli.rst b/docs/source/cli.rst
index 9397bb4..fc83181 100644
--- a/docs/source/cli.rst
+++ b/docs/source/cli.rst
@@ -9,33 +9,7 @@ When git-machete is installed, it adds ``machete`` command to ``git``, so it can
 
 git machete commands and help topics:
 
-* :ref:`add`              -- Add a branch to the tree of branch dependencies.
-* :ref:`advance`          -- Fast-forward merge one of children to the current branch and then slide out this child
-* :ref:`anno`             -- Manage custom annotations
-* :ref:`config`           -- Display docs for config keys and environment variables
-* :ref:`clean`            -- Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs
-* :ref:`delete-unmanaged` -- Delete local branches that are not present in the definition file
-* :ref:`diff`             -- Diff current working directory or a given branch against its computed fork point
-* :ref:`discover`         -- Automatically discover tree of branch dependencies
-* :ref:`edit`             -- Edit the definition file
-* :ref:`file`             -- Display the location of the definition file
-* :ref:`fork-point`       -- Display or override fork point for a branch
-* :ref:`format`           -- Display docs for the format of the definition file
-* :ref:`github`           -- Create, check out and manage GitHub PRs while keeping them reflected in git machete
-* :ref:`go`               -- Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up <annotation text>
-* :ref:`help`             -- Display this overview, or detailed help for a specified command
-* :ref:`hooks`            -- Display docs for the extra hooks added by git machete
-* :ref:`is-managed`       -- Check if the current branch is managed by git machete (mostly for scripts)
-* :ref:`list`             -- List all branches that fall into one of pre-defined categories (mostly for internal use)
-* :ref:`log`              -- Log the part of history specific to the given branch
-* :ref:`reapply`          -- Rebase the current branch onto its computed fork point
-* :ref:`show`             -- Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up <annotation text>
-* :ref:`slide-out`        -- Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge
-* :ref:`squash`           -- Squash the unique history of the current branch into a single commit
-* :ref:`status`           -- Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote
-* :ref:`traverse`         -- Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one
-* :ref:`update`           -- Sync the current branch with its upstream (parent) branch via rebase or merge
-* :ref:`version`          -- Display the version and exit
+.. include:: short_docs.rst
 
 To get help via CLI run:
 
diff --git a/docs/source/short_docs.rst b/docs/source/short_docs.rst
new file mode 100644
index 0000000..ab167ad
--- /dev/null
+++ b/docs/source/short_docs.rst
@@ -0,0 +1,29 @@
+.. _short_docs:
+
+* :ref:`add`              -- Add a branch to the tree of branch dependencies
+* :ref:`advance`          -- Fast-forward merge one of children to the current branch, push it and then slide out the child
+* :ref:`anno`             -- Manage custom annotations
+* :ref:`clean`            -- Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs
+* :ref:`config`           -- Display docs for the git machete configuration keys and environment variables
+* :ref:`delete-unmanaged` -- Delete local branches that are not present in the definition file
+* :ref:`diff`             -- Diff current working directory or a given branch against its computed fork point
+* :ref:`discover`         -- Automatically discover tree of branch dependencies
+* :ref:`edit`             -- Edit the definition file
+* :ref:`file`             -- Display the location of the definition file
+* :ref:`fork-point`       -- Display or override fork point for a branch
+* :ref:`format`           -- Display docs for the format of the definition file
+* :ref:`github`           -- Create, check out and manage GitHub PRs while keeping them reflected in git machete
+* :ref:`go`               -- Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up argument
+* :ref:`help`             -- Display this overview, or detailed help for a specified command
+* :ref:`hooks`            -- Display docs for the extra hooks added by git machete
+* :ref:`is-managed`       -- Check if the current branch is managed by git machete (mostly for scripts)
+* :ref:`list`             -- List all branches that fall into one of pre-defined categories (mostly for internal use)
+* :ref:`log`              -- Log the part of history specific to the given branch
+* :ref:`reapply`          -- Rebase the current branch onto its computed fork point
+* :ref:`show`             -- Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up argument
+* :ref:`slide-out`        -- Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge
+* :ref:`squash`           -- Squash the unique history of the current branch into a single commit
+* :ref:`status`           -- Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote
+* :ref:`traverse`         -- Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one. By default starts from current branch
+* :ref:`update`           -- Sync the current branch with its upstream (parent) branch via rebase or merge
+* :ref:`version`          -- Display the version and exit
diff --git a/git_machete/__init__.py b/git_machete/__init__.py
index 4057eed..9e75ce1 100644
--- a/git_machete/__init__.py
+++ b/git_machete/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '3.12.5'
+__version__ = '3.12.6'
diff --git a/git_machete/cli.py b/git_machete/cli.py
index 4b3d64b..373675b 100644
--- a/git_machete/cli.py
+++ b/git_machete/cli.py
@@ -11,8 +11,7 @@ import git_machete.options
 from git_machete import __version__
 from git_machete import utils
 from git_machete.client import MacheteClient
-from git_machete.generated_long_docs import long_docs
-from git_machete.handmade_short_docs import short_docs
+from git_machete.generated_docs import short_docs, long_docs
 from git_machete.exceptions import MacheteException, StopInteraction
 from git_machete.git_operations import AnyBranchName, AnyRevision, GitContext, LocalBranchShortName, RemoteBranchShortName
 from git_machete.utils import bold, fmt, underline, excluding, warn
diff --git a/git_machete/generated_long_docs.py b/git_machete/generated_docs.py
similarity index 95%
rename from git_machete/generated_long_docs.py
rename to git_machete/generated_docs.py
index 14b6159..2c82321 100644
--- a/git_machete/generated_long_docs.py
+++ b/git_machete/generated_docs.py
@@ -1,9 +1,39 @@
 from typing import Dict
 
 # ---------------------------------------------------------------------------------------------------------
-# Warning: This file is NOT supposed to be edited directly, but instead regenerated via tox -e docs
+# Warning: This file is NOT supposed to be edited directly, but instead regenerated via `tox -e py-docs`
 # ---------------------------------------------------------------------------------------------------------
 
+short_docs: Dict[str, str] = {
+    "add": "Add a branch to the tree of branch dependencies",
+    "advance": "Fast-forward merge one of children to the current branch, push it and then slide out the child",
+    "anno": "Manage custom annotations",
+    "clean": "Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs",
+    "config": "Display docs for the git machete configuration keys and environment variables",
+    "delete-unmanaged": "Delete local branches that are not present in the definition file",
+    "diff": "Diff current working directory or a given branch against its computed fork point",
+    "discover": "Automatically discover tree of branch dependencies",
+    "edit": "Edit the definition file",
+    "file": "Display the location of the definition file",
+    "fork-point": "Display or override fork point for a branch",
+    "format": "Display docs for the format of the definition file",
+    "github": "Create, check out and manage GitHub PRs while keeping them reflected in git machete",
+    "go": "Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up argument",
+    "help": "Display this overview, or detailed help for a specified command",
+    "hooks": "Display docs for the extra hooks added by git machete",
+    "is-managed": "Check if the current branch is managed by git machete (mostly for scripts)",
+    "list": "List all branches that fall into one of pre-defined categories (mostly for internal use)",
+    "log": "Log the part of history specific to the given branch",
+    "reapply": "Rebase the current branch onto its computed fork point",
+    "show": "Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up argument",
+    "slide-out": "Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge",
+    "squash": "Squash the unique history of the current branch into a single commit",
+    "status": "Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote",
+    "traverse": "Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one. By default starts from current branch",
+    "update": "Sync the current branch with its upstream (parent) branch via rebase or merge",
+    "version": "Display the version and exit",
+}
+
 long_docs: Dict[str, str] = {
     "add": """
         <b>Usage:</b><b>
diff --git a/git_machete/handmade_short_docs.py b/git_machete/handmade_short_docs.py
deleted file mode 100644
index 828cfa7..0000000
--- a/git_machete/handmade_short_docs.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from typing import Dict
-
-# ---------------------------------------------------------------------------------------------------------
-# This file is supposed to be modified manually
-# ---------------------------------------------------------------------------------------------------------
-
-short_docs: Dict[str, str] = {
-    "add": "Add a branch to the tree of branch dependencies",
-    "advance": "Fast-forward merge one of children to the current branch, push it and then slide out the child",
-    "anno": "Manage custom annotations",
-    "clean": "Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs",
-    "config": "Display docs for the git machete configuration keys and environment variables",
-    "delete-unmanaged": "Delete local branches that are not present in the definition file",
-    "diff": "Diff current working directory or a given branch against its computed fork point",
-    "discover": "Automatically discover tree of branch dependencies",
-    "edit": "Edit the definition file",
-    "file": "Display the location of the definition file",
-    "fork-point": "Display or override fork point for a branch",
-    "format": "Display docs for the format of the definition file",
-    "github": "Create, check out and manage GitHub PRs while keeping them reflected in git machete",
-    "go": "Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up argument",
-    "help": "Display this overview, or detailed help for a specified command",
-    "hooks": "Display docs for the extra hooks added by git machete",
-    "is-managed": "Check if the current branch is managed by git machete (mostly for scripts)",
-    "list": "List all branches that fall into one of pre-defined categories (mostly for internal use)",
-    "log": "Log the part of history specific to the given branch",
-    "reapply": "Rebase the current branch onto its computed fork point",
-    "show": "Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up argument",
-    "slide-out": "Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge",
-    "squash": "Squash the unique history of the current branch into a single commit",
-    "status": "Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote",
-    "traverse": "Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one. By default starts from current branch",
-    "update": "Sync the current branch with its upstream (parent) branch via rebase or merge",
-    "version": "Display the version and exit"
-}
diff --git a/tox.ini b/tox.ini
index 1ca63d9..4d14117 100644
--- a/tox.ini
+++ b/tox.ini
@@ -32,7 +32,7 @@ commands = flake8
 ignore = W504
 show-source = True
 max-line-length = 140
-exclude = ./.*,build,dist,*egg,venv, git_machete/handmade_short_docs.py, git_machete/generated_long_docs.py
+exclude = ./.*,build,dist,*egg,venv, git_machete/generated_docs.py
 import-order-style = pep8
 
 [testenv:venv]
@@ -69,7 +69,7 @@ allowlist_externals = bash
 deps =
   -r{toxinidir}/requirements.py-docs.txt
 commands =
-  bash -c "python docs/generate_py_docs.py > git_machete/generated_long_docs.py"
+  bash -c "python docs/generate_py_docs.py > git_machete/generated_docs.py"
 
 [testenv:check-py-docs]
 description = "Check if Python documentation is up to date with Sphinx documentation"


Test patch:
diff --git a/tests/test_help.py b/tests/test_help.py
index acce166..51ae13a 100644
--- a/tests/test_help.py
+++ b/tests/test_help.py
@@ -2,7 +2,7 @@ from typing import Any, List
 
 import pytest
 
-from git_machete.generated_long_docs import long_docs
+from git_machete.generated_docs import long_docs
 
 from unittest import mock