|
| 1 | +#!/usr/bin/env python |
| 2 | +""" |
| 3 | +Manage translation for Python documentation |
| 4 | +""" |
| 5 | + |
| 6 | +# SPDX-License-Identifier: CC0-1.0 |
| 7 | + |
| 8 | + |
| 9 | +importargparse |
| 10 | +importlogging |
| 11 | +importos |
| 12 | +importshutil |
| 13 | +importsubprocess |
| 14 | +importsys |
| 15 | +frompathlibimportPath |
| 16 | +fromtypingimportOptional |
| 17 | + |
| 18 | +ROOTDIR=Path(__file__).resolve().parent.parent |
| 19 | +COMMANDS= { |
| 20 | +"build", |
| 21 | +} |
| 22 | + |
| 23 | +# Logger configuration |
| 24 | +logging.basicConfig( |
| 25 | +level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s" |
| 26 | +) |
| 27 | +logger=logging.getLogger(__name__) |
| 28 | + |
| 29 | + |
| 30 | +defconfigure_parser()->argparse.ArgumentParser: |
| 31 | +parser=argparse.ArgumentParser(description=__doc__) |
| 32 | +parser.add_argument("command",choices=COMMANDS,help="The command to execute") |
| 33 | +parser.add_argument( |
| 34 | +"--language","-l",help="Language for the translated documentation" |
| 35 | + ) |
| 36 | +parser.add_argument("--python-version","-v",help="Python version to be used") |
| 37 | +parser.add_argument( |
| 38 | +"--logs-dir", |
| 39 | +"-L", |
| 40 | +default=ROOTDIR/"logs", |
| 41 | +help="Directory for logs (default: 'logs')", |
| 42 | + ) |
| 43 | +parser.add_argument( |
| 44 | +"--cpython-path", |
| 45 | +"-c", |
| 46 | +default=ROOTDIR/"cpython", |
| 47 | +type=Path, |
| 48 | +help="Path to the CPython repository (default: 'cpython')", |
| 49 | + ) |
| 50 | +returnparser |
| 51 | + |
| 52 | + |
| 53 | +defget_value(env_var_name:str,arg_value:Optional[str])->str: |
| 54 | +""" |
| 55 | + Return value passed via command-line interface arguments *arg_value*, |
| 56 | + and if not passed, use the environment variable *env_var_name* instead. |
| 57 | + """ |
| 58 | + |
| 59 | +value=arg_valueoros.getenv(env_var_name) |
| 60 | +ifnotvalue: |
| 61 | +logger.error( |
| 62 | +f"The environment variable{env_var_name} is not defined, and no value was provided by the command line." |
| 63 | + ) |
| 64 | +sys.exit(1) |
| 65 | +returnvalue |
| 66 | + |
| 67 | + |
| 68 | +defget_minor_version(version:str)->int: |
| 69 | +"""Return Python minor *version* assuming the schema as X.Y, e.g. 3.13""" |
| 70 | +returnint(version.split(".")[1]) |
| 71 | + |
| 72 | + |
| 73 | +defbuild(language:str,version:str,logs_dir:Path,cpython_path:Path)->None: |
| 74 | +minor_version=get_minor_version(version) |
| 75 | +warning_log=logs_dir/"sphinxwarnings.txt" |
| 76 | + |
| 77 | +# Sphinx options. |
| 78 | +# Append gettext_compact=False if python version is 3.11 or older, |
| 79 | +# because this confval was added to conf.py only in 3.12. |
| 80 | +opts=f"-E -D language={language} --keep-going -w{warning_log}" |
| 81 | +ifminor_version<12: |
| 82 | +opts+=" -D gettext_compact=False" |
| 83 | + |
| 84 | +try: |
| 85 | +# Run the make command |
| 86 | +logger.info( |
| 87 | +f"Building documentation for language{language}, Python version{version}." |
| 88 | + ) |
| 89 | +subprocess.run( |
| 90 | + ["make","-C",cpython_path/"Doc","html",f"SPHINXOPTS={opts}"], |
| 91 | +check=True, |
| 92 | + ) |
| 93 | +exceptsubprocess.CalledProcessErrorase: |
| 94 | +logger.error(f"Error executing the make command:{e}") |
| 95 | +raise |
| 96 | + |
| 97 | +# Remove the warning log file if it is empty |
| 98 | +ifwarning_log.exists()andwarning_log.stat().st_size==0: |
| 99 | +warning_log.unlink() |
| 100 | +logger.info("The warning log file was empty and has been removed.") |
| 101 | + |
| 102 | + |
| 103 | +defmain()->None: |
| 104 | +# Configure ArgumentParser |
| 105 | +parser=configure_parser() |
| 106 | +args=parser.parse_args() |
| 107 | + |
| 108 | +# Get values from environment variables or arguments |
| 109 | +language=get_value("PYDOC_LANGUAGE",args.language) |
| 110 | +version=get_value("PYDOC_VERSION",args.python_version) |
| 111 | +logs_dir=Path(get_value("PYDOC_LOGS",str(args.logs_dir))) |
| 112 | +cpython_path=args.cpython_path |
| 113 | + |
| 114 | +# Validate contents of the CPython local checkout |
| 115 | +conf_file=cpython_path/"Doc"/"conf.py" |
| 116 | +ifnotconf_file.exists(): |
| 117 | +logger.error( |
| 118 | +f"Configuration file '{conf_file}' not found. Invalid CPython checkout directory." |
| 119 | + ) |
| 120 | +sys.exit(1) |
| 121 | + |
| 122 | +# Check if the command is one of those that use Sphinx |
| 123 | +ifargs.commandin [ |
| 124 | +"build", |
| 125 | + ]: |
| 126 | +# make is required |
| 127 | +ifnotshutil.which("make"): |
| 128 | +logger.error("Executable 'make' not found, make sure it is installed.") |
| 129 | +sys.exit(1) |
| 130 | + |
| 131 | +# Create the logs directory if it doesn't exist |
| 132 | +logs_dir.mkdir(exist_ok=True) |
| 133 | +logger.info(f"Logs will be stored in the directory:{logs_dir}") |
| 134 | + |
| 135 | +ifargs.command=="build": |
| 136 | +# Build the documentation |
| 137 | +try: |
| 138 | +build(language,version,logs_dir,cpython_path) |
| 139 | +logger.info("Documentation built successfully.") |
| 140 | +exceptExceptionase: |
| 141 | +logger.error(f"Error building the documentation:{e}") |
| 142 | +raise |
| 143 | + |
| 144 | + |
| 145 | +if__name__=="__main__": |
| 146 | +main() |