Cleanup: minor improvements & type hints for man-page generator

- Use main() function.
- Use argparse for parsing arguments.
- Keep under 120 column width.
- Use type hints (passes `mypy --strict`).
This commit is contained in:
Campbell Barton 2021-04-10 15:30:29 +10:00
parent 0515ff70ec
commit f337310b43
Notes: blender-bot 2023-02-14 05:50:03 +01:00
Referenced by issue #87409, Current state of type hinting:
Referenced by issue #87388, In Collada-imported materials, node links red, no Surface in Properties Editor
2 changed files with 121 additions and 64 deletions

View File

@ -22,7 +22,7 @@
This script generates the blender.1 man page, embedding the help text
from the Blender executable itself. Invoke it as follows:
blender.1.py <path-to-blender> <output-filename>
blender.1.py --blender <path-to-blender> --output <output-filename>
where <path-to-blender> is the path to the Blender executable,
and <output-filename> is where to write the generated man page.
@ -30,108 +30,137 @@ and <output-filename> is where to write the generated man page.
# <pep8 compliant>
import argparse
import os
import subprocess
import sys
import time
from typing import (
Dict,
TextIO,
)
def man_format(data):
def man_format(data: str) -> str:
data = data.replace("-", "\\-")
data = data.replace("\t", " ")
return data
if len(sys.argv) != 3:
import getopt
raise getopt.GetoptError("Usage: %s <path-to-blender> <output-filename>" % sys.argv[0])
def blender_extract_info(blender_bin: str) -> Dict[str, str]:
blender_bin = sys.argv[1]
outfilename = sys.argv[2]
blender_env={
"ASAN_OPTIONS": "exitcode=0:" + os.environ.get("ASAN_OPTIONS", ""),
}
cmd = [blender_bin, "--help"]
print(" executing:", " ".join(cmd))
ASAN_OPTIONS = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
blender_help = subprocess.run(
cmd, env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8")
blender_version = subprocess.run(
[blender_bin, "--version"], env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8").strip()
blender_version, blender_date = (blender_version.split("build") + [None, None])[0:2]
blender_version = blender_version.rstrip().partition(" ")[2] # remove 'Blender' prefix.
if blender_date is None:
# Happens when built without WITH_BUILD_INFO e.g.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else:
blender_date = blender_date.strip().partition(" ")[2] # remove 'date:' prefix
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
blender_help = subprocess.run(
[blender_bin, "--help"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8")
outfile = open(outfilename, "w")
fw = outfile.write
blender_version = subprocess.run(
[blender_bin, "--version"],
env=blender_env,
check=True,
stdout=subprocess.PIPE,
).stdout.decode(encoding="utf-8").strip()
fw('.TH "BLENDER" "1" "%s" "Blender %s"\n' % (date_string, blender_version.replace(".", "\\&.")))
blender_version, blender_date = (blender_version.split("build") + ["", ""])[0:2]
blender_version = blender_version.rstrip().partition(" ")[2] # Remove 'Blender' prefix.
fw('''
if not blender_date:
# Happens when built without WITH_BUILD_INFO e.g.
date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
else:
blender_date = blender_date.strip().partition(" ")[2] # Remove 'date:' prefix.
date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
return {
"help": blender_help,
"version": blender_version,
"date": date_string,
}
def man_page_from_blender_help(fh: TextIO, blender_bin: str) -> None:
blender_info = blender_extract_info(blender_bin)
# Header Content.
fh.write(
'.TH "BLENDER" "1" "%s" "Blender %s"\n' %
(blender_info["date"], blender_info["version"].replace(".", "\\&."))
)
fh.write('''
.SH NAME
blender \- a full-featured 3D application''')
fw('''
fh.write('''
.SH SYNOPSIS
.B blender [args ...] [file] [args ...]''')
fw('''
fh.write('''
.br
.SH DESCRIPTION
.PP
.B blender
is a full-featured 3D application. It supports the entirety of the 3D pipeline - modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
is a full-featured 3D application. It supports the entirety of the 3D pipeline - \
modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
Use Blender to create 3D images and animations, films and commercials, content for games, architectural and industrial visualizatons, and scientific visualizations.
Use Blender to create 3D images and animations, films and commercials, content for games, \
architectural and industrial visualizatons, and scientific visualizations.
https://www.blender.org''')
fw('''
fh.write('''
.SH OPTIONS''')
fw("\n\n")
fh.write("\n\n")
lines = [line.rstrip() for line in blender_help.split("\n")]
# Body Content.
while lines:
l = lines.pop(0)
if l.startswith("Environment Variables:"):
fw('.SH "ENVIRONMENT VARIABLES"\n')
elif l.endswith(":"): # one line
fw('.SS "%s"\n\n' % l)
elif l.startswith("-") or l.startswith("/"): # can be multi line
lines = [line.rstrip() for line in blender_info["help"].split("\n")]
fw('.TP\n')
fw('.B %s\n' % man_format(l))
while lines:
l = lines.pop(0)
if l.startswith("Environment Variables:"):
fh.write('.SH "ENVIRONMENT VARIABLES"\n')
elif l.endswith(":"): # One line.
fh.write('.SS "%s"\n\n' % l)
elif l.startswith("-") or l.startswith("/"): # Can be multi line.
fh.write('.TP\n')
fh.write('.B %s\n' % man_format(l))
while lines:
# line with no
if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # no white space
break
while lines:
# line with no
if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # No white space.
break
if not l: # second blank line
fw('.IP\n')
else:
fw('.br\n')
if not l: # Second blank line.
fh.write('.IP\n')
else:
fh.write('.br\n')
l = lines.pop(0)
l = l[1:] # remove first whitespace (tab)
l = lines.pop(0)
if l:
assert(l.startswith('\t'))
l = l[1:] # Remove first white-space (tab).
fw('%s\n' % man_format(l))
fh.write('%s\n' % man_format(l))
else:
if not l.strip():
fw('.br\n')
else:
fw('%s\n' % man_format(l))
if not l.strip():
fh.write('.br\n')
else:
fh.write('%s\n' % man_format(l))
# footer
# Footer Content.
fw('''
fh.write(
'''
.br
.SH SEE ALSO
.B luxrender(1)
@ -143,5 +172,33 @@ This manpage was written for a Debian GNU/Linux system by Daniel Mester
<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
''')
outfile.close()
print("written:", outfilename)
def create_argparse() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument(
"--output",
required=True,
help="The man page to write to."
)
parser.add_argument(
"--blender",
required=True,
help="Path to the blender binary."
)
return parser
def main() -> None:
parser = create_argparse()
args = parser.parse_args()
blender_bin = args.blender
output_filename = args.output
with open(output_filename, "w", encoding="utf-8") as fh:
man_page_from_blender_help(fh, blender_bin)
if __name__ == "__main__":
main()

View File

@ -463,8 +463,8 @@ if(UNIX AND NOT APPLE)
add_custom_target(
blender_man_page ALL
COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
${EXECUTABLE_OUTPUT_PATH}/blender
${CMAKE_CURRENT_BINARY_DIR}/blender.1)
--blender ${EXECUTABLE_OUTPUT_PATH}/blender
--output ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
add_dependencies(blender_man_page blender)
endif()
endif()