Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scripts: fix all issues reported by pylint

This patch 1) fixes all the issues (not most) reported by pylint,
2) add the functionability to tackle documents that need translation,
3) add logging to adjust the logging level and log file

Signed-off-by: Dongliang Mu <dzm91@hust.edu.cn>
Reviewed-by: Yanteng Si <siyanteng@loongson.cn>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/20240719041400.3909775-2-dzm91@hust.edu.cn

authored by

Dongliang Mu and committed by
Jonathan Corbet
63e96ce0 565a3041

+140 -72
+140 -72
scripts/checktransupdate.py
··· 10 10 11 11 The usage is as follows: 12 12 - ./scripts/checktransupdate.py -l zh_CN 13 - This will print all the files that need to be updated in the zh_CN locale. 13 + This will print all the files that need to be updated or translated in the zh_CN locale. 14 14 - ./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst 15 15 This will only print the status of the specified file. 16 16 17 17 The output is something like: 18 - Documentation/translations/zh_CN/dev-tools/testing-overview.rst (1 commits) 18 + Documentation/dev-tools/kfence.rst 19 + No translation in the locale of zh_CN 20 + 21 + Documentation/translations/zh_CN/dev-tools/testing-overview.rst 19 22 commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs") 23 + 1 commits needs resolving in total 20 24 """ 21 25 22 26 import os 23 - from argparse import ArgumentParser, BooleanOptionalAction 27 + import time 28 + import logging 29 + from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction 24 30 from datetime import datetime 25 - 26 - flag_p_c = False 27 - flag_p_uf = False 28 - flag_debug = False 29 - 30 - 31 - def dprint(*args, **kwargs): 32 - if flag_debug: 33 - print("[DEBUG] ", end="") 34 - print(*args, **kwargs) 35 31 36 32 37 33 def get_origin_path(file_path): 34 + """Get the origin path from the translation path""" 38 35 paths = file_path.split("/") 39 36 tidx = paths.index("translations") 40 37 opaths = paths[:tidx] ··· 40 43 41 44 42 45 def get_latest_commit_from(file_path, commit): 43 - command = "git log --pretty=format:%H%n%aD%n%cD%n%n%B {} -1 -- {}".format( 44 - commit, file_path 45 - ) 46 - dprint(command) 46 + """Get the latest commit from the specified commit for the specified file""" 47 + command = f"git log --pretty=format:%H%n%aD%n%cD%n%n%B {commit} -1 -- {file_path}" 48 + logging.debug(command) 47 49 pipe = os.popen(command) 48 50 result = pipe.read() 49 51 result = result.split("\n") 50 52 if len(result) <= 1: 51 53 return None 52 54 53 - dprint("Result: {}".format(result[0])) 55 + logging.debug("Result: %s", result[0]) 54 56 55 57 return { 56 58 "hash": result[0], ··· 60 64 61 65 62 66 def get_origin_from_trans(origin_path, t_from_head): 67 + """Get the latest origin commit from the translation commit""" 63 68 o_from_t = get_latest_commit_from(origin_path, t_from_head["hash"]) 64 69 while o_from_t is not None and o_from_t["author_date"] > t_from_head["author_date"]: 65 70 o_from_t = get_latest_commit_from(origin_path, o_from_t["hash"] + "^") 66 71 if o_from_t is not None: 67 - dprint("tracked origin commit id: {}".format(o_from_t["hash"])) 72 + logging.debug("tracked origin commit id: %s", o_from_t["hash"]) 68 73 return o_from_t 69 74 70 75 71 76 def get_commits_count_between(opath, commit1, commit2): 72 - command = "git log --pretty=format:%H {}...{} -- {}".format(commit1, commit2, opath) 73 - dprint(command) 77 + """Get the commits count between two commits for the specified file""" 78 + command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}" 79 + logging.debug(command) 74 80 pipe = os.popen(command) 75 81 result = pipe.read().split("\n") 76 82 # filter out empty lines ··· 81 83 82 84 83 85 def pretty_output(commit): 84 - command = "git log --pretty='format:%h (\"%s\")' -1 {}".format(commit) 85 - dprint(command) 86 + """Pretty print the commit message""" 87 + command = f"git log --pretty='format:%h (\"%s\")' -1 {commit}" 88 + logging.debug(command) 86 89 pipe = os.popen(command) 87 90 return pipe.read() 88 91 89 92 93 + def valid_commit(commit): 94 + """Check if the commit is valid or not""" 95 + msg = pretty_output(commit) 96 + return "Merge tag" not in msg 97 + 90 98 def check_per_file(file_path): 99 + """Check the translation status for the specified file""" 91 100 opath = get_origin_path(file_path) 92 101 93 102 if not os.path.isfile(opath): 94 - dprint("Error: Cannot find the origin path for {}".format(file_path)) 103 + logging.error("Cannot find the origin path for {file_path}") 95 104 return 96 105 97 106 o_from_head = get_latest_commit_from(opath, "HEAD") 98 107 t_from_head = get_latest_commit_from(file_path, "HEAD") 99 108 100 109 if o_from_head is None or t_from_head is None: 101 - print("Error: Cannot find the latest commit for {}".format(file_path)) 110 + logging.error("Cannot find the latest commit for %s", file_path) 102 111 return 103 112 104 113 o_from_t = get_origin_from_trans(opath, t_from_head) 105 114 106 115 if o_from_t is None: 107 - print("Error: Cannot find the latest origin commit for {}".format(file_path)) 116 + logging.error("Error: Cannot find the latest origin commit for %s", file_path) 108 117 return 109 118 110 119 if o_from_head["hash"] == o_from_t["hash"]: 111 - if flag_p_uf: 112 - print("No update needed for {}".format(file_path)) 113 - return 120 + logging.debug("No update needed for %s", file_path) 114 121 else: 115 - print("{}".format(file_path), end="\t") 122 + logging.info(file_path) 116 123 commits = get_commits_count_between( 117 124 opath, o_from_t["hash"], o_from_head["hash"] 118 125 ) 119 - print("({} commits)".format(len(commits))) 120 - if flag_p_c: 121 - for commit in commits: 122 - msg = pretty_output(commit) 123 - if "Merge tag" not in msg: 124 - print("commit", msg) 126 + count = 0 127 + for commit in commits: 128 + if valid_commit(commit): 129 + logging.info("commit %s", pretty_output(commit)) 130 + count += 1 131 + logging.info("%d commits needs resolving in total\n", count) 132 + 133 + 134 + def valid_locales(locale): 135 + """Check if the locale is valid or not""" 136 + script_path = os.path.dirname(os.path.abspath(__file__)) 137 + linux_path = os.path.join(script_path, "..") 138 + if not os.path.isdir(f"{linux_path}/Documentation/translations/{locale}"): 139 + raise ArgumentTypeError("Invalid locale: {locale}") 140 + return locale 141 + 142 + 143 + def list_files_with_excluding_folders(folder, exclude_folders, include_suffix): 144 + """List all files with the specified suffix in the folder and its subfolders""" 145 + files = [] 146 + stack = [folder] 147 + 148 + while stack: 149 + pwd = stack.pop() 150 + # filter out the exclude folders 151 + if os.path.basename(pwd) in exclude_folders: 152 + continue 153 + # list all files and folders 154 + for item in os.listdir(pwd): 155 + ab_item = os.path.join(pwd, item) 156 + if os.path.isdir(ab_item): 157 + stack.append(ab_item) 158 + else: 159 + if ab_item.endswith(include_suffix): 160 + files.append(ab_item) 161 + 162 + return files 163 + 164 + 165 + class DmesgFormatter(logging.Formatter): 166 + """Custom dmesg logging formatter""" 167 + def format(self, record): 168 + timestamp = time.time() 169 + formatted_time = f"[{timestamp:>10.6f}]" 170 + log_message = f"{formatted_time} {record.getMessage()}" 171 + return log_message 172 + 173 + 174 + def config_logging(log_level, log_file="checktransupdate.log"): 175 + """configure logging based on the log level""" 176 + # set up the root logger 177 + logger = logging.getLogger() 178 + logger.setLevel(log_level) 179 + 180 + # Create console handler 181 + console_handler = logging.StreamHandler() 182 + console_handler.setLevel(log_level) 183 + 184 + # Create file handler 185 + file_handler = logging.FileHandler(log_file) 186 + file_handler.setLevel(log_level) 187 + 188 + # Create formatter and add it to the handlers 189 + formatter = DmesgFormatter() 190 + console_handler.setFormatter(formatter) 191 + file_handler.setFormatter(formatter) 192 + 193 + # Add the handler to the logger 194 + logger.addHandler(console_handler) 195 + logger.addHandler(file_handler) 125 196 126 197 127 198 def main(): 199 + """Main function of the script""" 128 200 script_path = os.path.dirname(os.path.abspath(__file__)) 129 201 linux_path = os.path.join(script_path, "..") 130 202 ··· 202 134 parser.add_argument( 203 135 "-l", 204 136 "--locale", 137 + default="zh_CN", 138 + type=valid_locales, 205 139 help="Locale to check when files are not specified", 206 140 ) 141 + 207 142 parser.add_argument( 208 - "--print-commits", 143 + "--print-missing-translations", 209 144 action=BooleanOptionalAction, 210 145 default=True, 211 - help="Print commits between the origin and the translation", 146 + help="Print files that do not have translations", 212 147 ) 213 148 214 149 parser.add_argument( 215 - "--print-updated-files", 216 - action=BooleanOptionalAction, 217 - default=False, 218 - help="Print files that do no need to be updated", 219 - ) 150 + '--log', 151 + default='INFO', 152 + choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], 153 + help='Set the logging level') 220 154 221 155 parser.add_argument( 222 - "--debug", 223 - action=BooleanOptionalAction, 224 - help="Print debug information", 225 - default=False, 226 - ) 156 + '--logfile', 157 + default='checktransupdate.log', 158 + help='Set the logging file (default: checktransupdate.log)') 227 159 228 160 parser.add_argument( 229 161 "files", nargs="*", help="Files to check, if not specified, check all files" 230 162 ) 231 163 args = parser.parse_args() 232 164 233 - global flag_p_c, flag_p_uf, flag_debug 234 - flag_p_c = args.print_commits 235 - flag_p_uf = args.print_updated_files 236 - flag_debug = args.debug 165 + # Configure logging based on the --log argument 166 + log_level = getattr(logging, args.log.upper(), logging.INFO) 167 + config_logging(log_level) 237 168 238 - # get files related to linux path 169 + # Get files related to linux path 239 170 files = args.files 240 171 if len(files) == 0: 241 - if args.locale is not None: 242 - files = ( 243 - os.popen( 244 - "find {}/Documentation/translations/{} -type f".format( 245 - linux_path, args.locale 246 - ) 247 - ) 248 - .read() 249 - .split("\n") 250 - ) 251 - else: 252 - files = ( 253 - os.popen( 254 - "find {}/Documentation/translations -type f".format(linux_path) 255 - ) 256 - .read() 257 - .split("\n") 258 - ) 172 + offical_files = list_files_with_excluding_folders( 173 + os.path.join(linux_path, "Documentation"), ["translations", "output"], "rst" 174 + ) 259 175 260 - files = list(filter(lambda x: x != "", files)) 176 + for file in offical_files: 177 + # split the path into parts 178 + path_parts = file.split(os.sep) 179 + # find the index of the "Documentation" directory 180 + kindex = path_parts.index("Documentation") 181 + # insert the translations and locale after the Documentation directory 182 + new_path_parts = path_parts[:kindex + 1] + ["translations", args.locale] \ 183 + + path_parts[kindex + 1 :] 184 + # join the path parts back together 185 + new_file = os.sep.join(new_path_parts) 186 + if os.path.isfile(new_file): 187 + files.append(new_file) 188 + else: 189 + if args.print_missing_translations: 190 + logging.info(os.path.relpath(os.path.abspath(file), linux_path)) 191 + logging.info("No translation in the locale of %s\n", args.locale) 192 + 261 193 files = list(map(lambda x: os.path.relpath(os.path.abspath(x), linux_path), files)) 262 194 263 195 # cd to linux root directory