From 553e93cd9b897f6af59170a9ee55a6e5d7b6d232 Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Tue, 24 Mar 2026 16:23:03 -0500 Subject: [PATCH 1/6] save --- src/rda_python_miscs/pg_rst.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index 0c7c4ad..2347148 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -19,6 +19,7 @@ import argparse import importlib from os import path as op +from rda_python_common.pg_log import PgLOG from rda_python_common.pg_file import PgFile from rda_python_common.pg_util import PgUtil @@ -1157,9 +1158,9 @@ def _load_opts_alias(docname): try: mod = importlib.import_module(modname) except ImportError as exc: - self.pglog( + PgLOG.pglog( "Cannot import module '{}': {}".format(modname, exc), - self.LGWNEX, + PgLOG.LGWNEX, ) # Derive ORIGIN from the module's own file path. @@ -1181,10 +1182,10 @@ def _load_opts_alias(docname): break if opts is None: - self.pglog( + PgLOG.pglog( "Module '{}' does not define OPTS (checked module level and " "all classes defined in the module)".format(modname), - self.LGWNEX, + PgLOG.LGWNEX, ) # ALIAS is optional; default to empty dict. From 324fccf4ab82082718d1fce79011bdec712e843f Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Tue, 24 Mar 2026 16:29:45 -0500 Subject: [PATCH 2/6] save --- src/rda_python_miscs/pg_rst.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index 2347148..898ab0c 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -19,7 +19,6 @@ import argparse import importlib from os import path as op -from rda_python_common.pg_log import PgLOG from rda_python_common.pg_file import PgFile from rda_python_common.pg_util import PgUtil @@ -142,17 +141,17 @@ def process_docs(self, docname, opts, alias): self.ALIAS = alias self.parse_docs(docname) - if not self.sections: self.pglog(docname + ": empty document", self.LGWNEX) + if not self.sections: PgLOG.pglog(docname + ": empty document", PgLOG.LGWNEX) self.DOCS['DOCNAM'] = docname if docname in self.LINKS: self.LINKS.remove(docname) self.DOCS['DOCLNK'] = r"({})".format('|'.join(self.LINKS)) self.DOCS['DOCTIT'] = docname.upper() - self.change_local_directory(self.DOCS['DOCDIR'], self.LGWNEX) - self.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), self.LOGWRN) + self.change_local_directory(self.DOCS['DOCDIR'], PgLOG.LGWNEX) + PgLOG.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), PgLOG.LOGWRN) if op.exists("index.rst"): # write index file once - self.pglog("index.rst exists already, delete first if needs to be regenerated", self.LOGWRN) + PgLOG.pglog("index.rst exists already, delete first if needs to be regenerated", PgLOG.LOGWRN) else: self.write_index(self.sections[0]) @@ -176,7 +175,7 @@ def parse_docs(self, docname): docname (str): Short document name used to locate ``/.usg``. """ docfile = "{}/{}.usg".format(self.DOCS['ORIGIN'], docname) - self.pglog("Parsing info for Document '{}'".format(docname), self.LOGWRN) + PgLOG.pglog("Parsing info for Document '{}'".format(docname), PgLOG.LOGWRN) section = self.init_section('0', "Preface") option = example = None fh = open(docfile, 'r') @@ -227,11 +226,11 @@ def parse_docs(self, docname): # check completion of options for opt in self.OPTS: if opt not in self.options: - self.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), self.LOGWRN) + PgLOG.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), PgLOG.LOGWRN) if self.sections: cnt = len(self.sections) s = 's' if cnt > 1 else '' - self.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), self.LOGWRN) + PgLOG.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), PgLOG.LOGWRN) # # cache section information @@ -369,7 +368,7 @@ def init_option(self, secid, opt, desc): types = ("Mode", "Info", "Info", "Action") if opt not in self.OPTS: - self.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), self.LGWNEX) + PgLOG.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), PgLOG.LGWNEX) option['secid'] = secid option['opt'] = opt ms = re.match(r'^(, | \(Alias: .*\), )(.*)', desc) @@ -489,15 +488,15 @@ def template_to_rst(self, template, hash, extra=None): matches = re.findall(r'__([A-Z]+)__', line) if matches: for key in matches: - if key not in hash: self.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), self.LGWNEX) - if not hash[key]: self.pglog(key + ": empty content", self.LGWNEX) + if key not in hash: PgLOG.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), PgLOG.LGWNEX) + if not hash[key]: PgLOG.pglog(key + ": empty content", PgLOG.LGWNEX) line = line.replace("__{}__".format(key), hash[key]) rf.write(line + "\n") line = tf.readline() tf.close() rf.close() - self.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), self.LOGWRN) + PgLOG.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), PgLOG.LOGWRN) # # create rst content for table of contents @@ -1080,7 +1079,7 @@ def get_short_option(self, p): for alias in self.ALIAS[opt]: if re.match(r'^{}$'.format(alias), p, re.I): return opt - self.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), self.LGWNEX) + PgLOG.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), PgLOG.LGWNEX) # # replace with rst link for a given section title @@ -1118,7 +1117,7 @@ def get_section(self, secid): for section in self.sections: if section['secid'] == secid: return section - self.pglog("Unknown Section ID {}".format(secid), self.LGWNEX) + PgLOG.pglog("Unknown Section ID {}".format(secid), PgLOG.LGWNEX) # --------------------------------------------------------------------------- From dbf6eb1b36922b7fa854a12b95e50d6283bad131 Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Wed, 25 Mar 2026 08:34:59 -0500 Subject: [PATCH 3/6] Move _load_opts_alias() into PgRST class as load_opts_alias() Co-Authored-By: Claude Sonnet 4.6 --- src/rda_python_miscs/pg_rst.py | 131 ++++++++++++++++----------------- 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index 6d6fad8..67e7a9e 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -1120,80 +1120,79 @@ def get_section(self, secid): PgLOG.pglog("Unknown Section ID {}".format(secid), PgLOG.LGWNEX) + def load_opts_alias(self, docname): + """Import ``rda_python_.`` and return its ``(OPTS, ALIAS, origin)`` triple. -# --------------------------------------------------------------------------- -# Command-line entry point -# --------------------------------------------------------------------------- - -def _load_opts_alias(docname): - """Import ``rda_python_.`` and return its ``(OPTS, ALIAS, origin)`` triple. + Resolution order for OPTS / ALIAS: - Resolution order for OPTS / ALIAS: + 1. Module-level ``OPTS`` / ``ALIAS`` attributes. + 2. The first class *defined in that module* that carries both ``OPTS`` + and ``ALIAS`` as class-level attributes. - 1. Module-level ``OPTS`` / ``ALIAS`` attributes. - 2. The first class *defined in that module* that carries both ``OPTS`` - and ``ALIAS`` as class-level attributes. + ``ALIAS`` is optional; an empty dict is returned when not found. - ``ALIAS`` is optional; an empty dict is returned when not found. + The ``origin`` value is the absolute path of the directory that contains + ``.py`` (i.e. ``rda_python_/``), derived from + ``mod.__file__``. It is intended to be assigned to + ``PgRST.DOCS['ORIGIN']`` so that :meth:`PgRST.parse_docs` looks for the + ``.usg`` source file in the same location as the document module. - The ``origin`` value is the absolute path of the directory that contains - ``.py`` (i.e. ``rda_python_/``), derived from - ``mod.__file__``. It is intended to be assigned to - ``PgRST.DOCS['ORIGIN']`` so that :meth:`PgRST.parse_docs` looks for the - ``.usg`` source file in the same location as the document module. - - Args: - docname (str): Short document name used to build the module path - ``rda_python_.``. - - Returns: - tuple[dict, dict, str]: ``(OPTS, ALIAS, origin)`` where *origin* is - the absolute directory path of the imported module file. - - Raises: - SystemExit: via :func:`PgLOG.pglog` (``LGWNEX``) if the module - cannot be imported or ``OPTS`` cannot be found. - """ - modname = "rda_python_{}.{}".format(docname, docname) - try: - mod = importlib.import_module(modname) - except ImportError as exc: - PgLOG.pglog( - "Cannot import module '{}': {}".format(modname, exc), - PgLOG.LGWNEX, - ) - - # Derive ORIGIN from the module's own file path. - origin = op.dirname(op.abspath(mod.__file__)) - - # 1. Try module-level attributes first. - opts = getattr(mod, 'OPTS', None) - alias = getattr(mod, 'ALIAS', None) - - # 2. Fall back to the first class in the module that owns both. - if opts is None or alias is None: - for _, obj in inspect.getmembers(mod, inspect.isclass): - if obj.__module__ == modname: - cls_opts = getattr(obj, 'OPTS', None) - cls_alias = getattr(obj, 'ALIAS', None) - if cls_opts is not None: - if opts is None: opts = cls_opts - if alias is None: alias = cls_alias - break + Args: + docname (str): Short document name used to build the module path + ``rda_python_.``. - if opts is None: - PgLOG.pglog( - "Module '{}' does not define OPTS (checked module level and " - "all classes defined in the module)".format(modname), - PgLOG.LGWNEX, - ) + Returns: + tuple[dict, dict, str]: ``(OPTS, ALIAS, origin)`` where *origin* is + the absolute directory path of the imported module file. - # ALIAS is optional; default to empty dict. - if alias is None: - alias = {} + Raises: + SystemExit: via :func:`PgLOG.pglog` (``LGWNEX``) if the module + cannot be imported or ``OPTS`` cannot be found. + """ + modname = "rda_python_{}.{}".format(docname, docname) + try: + mod = importlib.import_module(modname) + except ImportError as exc: + PgLOG.pglog( + "Cannot import module '{}': {}".format(modname, exc), + PgLOG.LGWNEX, + ) + + # Derive ORIGIN from the module's own file path. + origin = op.dirname(op.abspath(mod.__file__)) + + # 1. Try module-level attributes first. + opts = getattr(mod, 'OPTS', None) + alias = getattr(mod, 'ALIAS', None) + + # 2. Fall back to the first class in the module that owns both. + if opts is None or alias is None: + for _, obj in inspect.getmembers(mod, inspect.isclass): + if obj.__module__ == modname: + cls_opts = getattr(obj, 'OPTS', None) + cls_alias = getattr(obj, 'ALIAS', None) + if cls_opts is not None: + if opts is None: opts = cls_opts + if alias is None: alias = cls_alias + break + + if opts is None: + PgLOG.pglog( + "Module '{}' does not define OPTS (checked module level and " + "all classes defined in the module)".format(modname), + PgLOG.LGWNEX, + ) + + # ALIAS is optional; default to empty dict. + if alias is None: + alias = {} + + return opts, alias, origin - return opts, alias, origin +# --------------------------------------------------------------------------- +# Command-line entry point +# --------------------------------------------------------------------------- def main(): """Entry point for command-line usage of pg_rst.py.""" @@ -1229,8 +1228,8 @@ def main(): ) args = parser.parse_args() - opts, alias, origin = _load_opts_alias(args.docname) pg = PgRST() + opts, alias, origin = pg.load_opts_alias(args.docname) pg.DOCS['ORIGIN'] = origin if args.docdir is not None: pg.DOCS['DOCDIR'] = args.docdir From b6ebb12ee53b8ac879a4c9b4d2349433dbbbaf19 Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Wed, 25 Mar 2026 08:38:38 -0500 Subject: [PATCH 4/6] Replace PgLOG.* with self.* in PgRST and drop PgLOG import Co-Authored-By: Claude Sonnet 4.6 --- src/rda_python_miscs/pg_rst.py | 35 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index 67e7a9e..ef4d557 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -19,7 +19,6 @@ import argparse import importlib from os import path as op -from rda_python_common.pg_LOG import PgLOG from rda_python_common.pg_file import PgFile from rda_python_common.pg_util import PgUtil @@ -142,17 +141,17 @@ def process_docs(self, docname, opts, alias): self.ALIAS = alias self.parse_docs(docname) - if not self.sections: PgLOG.pglog(docname + ": empty document", PgLOG.LGWNEX) + if not self.sections: self.pglog(docname + ": empty document", self.LGWNEX) self.DOCS['DOCNAM'] = docname if docname in self.LINKS: self.LINKS.remove(docname) self.DOCS['DOCLNK'] = r"({})".format('|'.join(self.LINKS)) self.DOCS['DOCTIT'] = docname.upper() - self.change_local_directory(self.DOCS['DOCDIR'], PgLOG.LGWNEX) - PgLOG.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), PgLOG.LOGWRN) + self.change_local_directory(self.DOCS['DOCDIR'], self.LGWNEX) + self.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), self.LOGWRN) if op.exists("index.rst"): # write index file once - PgLOG.pglog("index.rst exists already, delete first if needs to be regenerated", PgLOG.LOGWRN) + self.pglog("index.rst exists already, delete first if needs to be regenerated", self.LOGWRN) else: self.write_index(self.sections[0]) @@ -176,7 +175,7 @@ def parse_docs(self, docname): docname (str): Short document name used to locate ``/.usg``. """ docfile = "{}/{}.usg".format(self.DOCS['ORIGIN'], docname) - PgLOG.pglog("Parsing info for Document '{}'".format(docname), PgLOG.LOGWRN) + self.pglog("Parsing info for Document '{}'".format(docname), self.LOGWRN) section = self.init_section('0', "Preface") option = example = None fh = open(docfile, 'r') @@ -227,11 +226,11 @@ def parse_docs(self, docname): # check completion of options for opt in self.OPTS: if opt not in self.options: - PgLOG.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), PgLOG.LOGWRN) + self.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), self.LOGWRN) if self.sections: cnt = len(self.sections) s = 's' if cnt > 1 else '' - PgLOG.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), PgLOG.LOGWRN) + self.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), self.LOGWRN) # # cache section information @@ -369,7 +368,7 @@ def init_option(self, secid, opt, desc): types = ("Mode", "Info", "Info", "Action") if opt not in self.OPTS: - PgLOG.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), PgLOG.LGWNEX) + self.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), self.LGWNEX) option['secid'] = secid option['opt'] = opt ms = re.match(r'^(, | \(Alias: .*\), )(.*)', desc) @@ -489,15 +488,15 @@ def template_to_rst(self, template, hash, extra=None): matches = re.findall(r'__([A-Z]+)__', line) if matches: for key in matches: - if key not in hash: PgLOG.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), PgLOG.LGWNEX) - if not hash[key]: PgLOG.pglog(key + ": empty content", PgLOG.LGWNEX) + if key not in hash: self.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), self.LGWNEX) + if not hash[key]: self.pglog(key + ": empty content", self.LGWNEX) line = line.replace("__{}__".format(key), hash[key]) rf.write(line + "\n") line = tf.readline() tf.close() rf.close() - PgLOG.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), PgLOG.LOGWRN) + self.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), self.LOGWRN) # # create rst content for table of contents @@ -1080,7 +1079,7 @@ def get_short_option(self, p): for alias in self.ALIAS[opt]: if re.match(r'^{}$'.format(alias), p, re.I): return opt - PgLOG.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), PgLOG.LGWNEX) + self.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), self.LGWNEX) # # replace with rst link for a given section title @@ -1118,7 +1117,7 @@ def get_section(self, secid): for section in self.sections: if section['secid'] == secid: return section - PgLOG.pglog("Unknown Section ID {}".format(secid), PgLOG.LGWNEX) + self.pglog("Unknown Section ID {}".format(secid), self.LGWNEX) def load_opts_alias(self, docname): """Import ``rda_python_.`` and return its ``(OPTS, ALIAS, origin)`` triple. @@ -1153,9 +1152,9 @@ def load_opts_alias(self, docname): try: mod = importlib.import_module(modname) except ImportError as exc: - PgLOG.pglog( + self.pglog( "Cannot import module '{}': {}".format(modname, exc), - PgLOG.LGWNEX, + self.LGWNEX, ) # Derive ORIGIN from the module's own file path. @@ -1177,10 +1176,10 @@ def load_opts_alias(self, docname): break if opts is None: - PgLOG.pglog( + self.pglog( "Module '{}' does not define OPTS (checked module level and " "all classes defined in the module)".format(modname), - PgLOG.LGWNEX, + self.LGWNEX, ) # ALIAS is optional; default to empty dict. From e11ba64ed5ec979c898037956790c56ca633a887 Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Wed, 25 Mar 2026 08:45:37 -0500 Subject: [PATCH 5/6] Clean up pg_rst.py: fix file handling, remove dead code, fix style - parse_docs, template_to_rst: use `with` for file I/O (safe close on exception) - replace_option_link: remove dead `if opts is None` guard (re.findall never returns None) - replace_option_link: remove stale comment referencing list.insert() - init_section: replace re.split dot-count with secid.count('.') + 1; init level directly in dict - init_example: return dict literal directly, drop redundant temp variable - create_description: use str.split instead of re.split for newline splitting - get_short_option: fix off-by-one indent on inner if (13 -> 12 spaces) - main: remove duplicate `import argparse` (already imported at module level) Co-Authored-By: Claude Sonnet 4.6 --- src/rda_python_miscs/pg_rst.py | 135 +++++++++++++++------------------ 1 file changed, 62 insertions(+), 73 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index ef4d557..06f7ec7 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -178,48 +178,47 @@ def parse_docs(self, docname): self.pglog("Parsing info for Document '{}'".format(docname), self.LOGWRN) section = self.init_section('0', "Preface") option = example = None - fh = open(docfile, 'r') - line = fh.readline() - while line: - if re.match(r'\s*#', line): - line = fh.readline() - continue # skip comment lines - ms = re.match(r'^(.*\S)\s+#', line) - if ms: - line = ms.group(1) # remove comments - else: - line = line.rstrip() # remove trailing white spaces - - # Temporarily escape tokens so they are not confused - # with special markers like <:>, <=>, used in option parsing. - while True: - ms = re.search(r'(<([A-Z/\-\.]+)>)', line) + with open(docfile, 'r') as fh: + line = fh.readline() + while line: + if re.match(r'\s*#', line): + line = fh.readline() + continue # skip comment lines + ms = re.match(r'^(.*\S)\s+#', line) if ms: - line = line.replace(ms.group(1), "<{}>".format(ms.group(2))) + line = ms.group(1) # remove comments else: - break - ms = re.match(r'^([\d\.]+)\s+(.+)$', line) - if ms: # start new section - section = self.record_section(section, option, example, ms.group(1), ms.group(2)) - option = example = None - else: - ms = re.match(r'^ -([A-Z]{2}) or -\w+(.*)$', line) - if ms: # found new option - option = self.record_option(section, option, example, ms.group(1), ms.group(2)) - example = None - elif option: - ms = re.match(r'^ For( | another )example, (.*)$', line) - if ms: # found example - example = self.record_example(option, example, ms.group(2)) - elif example: - example['desc'] += line + "\n" + line = line.rstrip() # remove trailing white spaces + + # Temporarily escape tokens so they are not confused + # with special markers like <:>, <=>, used in option parsing. + while True: + ms = re.search(r'(<([A-Z/\-\.]+)>)', line) + if ms: + line = line.replace(ms.group(1), "<{}>".format(ms.group(2))) else: - option['desc'] += line + "\n" + break + ms = re.match(r'^([\d\.]+)\s+(.+)$', line) + if ms: # start new section + section = self.record_section(section, option, example, ms.group(1), ms.group(2)) + option = example = None else: - section['desc'] += line + "\n" + ms = re.match(r'^ -([A-Z]{2}) or -\w+(.*)$', line) + if ms: # found new option + option = self.record_option(section, option, example, ms.group(1), ms.group(2)) + example = None + elif option: + ms = re.match(r'^ For( | another )example, (.*)$', line) + if ms: # found example + example = self.record_example(option, example, ms.group(2)) + elif example: + example['desc'] += line + "\n" + else: + option['desc'] += line + "\n" + else: + section['desc'] += line + "\n" - line = fh.readline() - fh.close() + line = fh.readline() self.record_section(section, option, example) @@ -322,15 +321,14 @@ def init_section(self, secid, title): dict: New section dict with keys ``secid``, ``title``, ``desc``, ``level``, and ``opts``. """ + level = secid.count('.') + 1 section = { 'secid' : secid, 'title' : title, 'desc' : "", - 'level' : 0, + 'level' : level, 'opts' : [] } - level = len(re.split(r'\.', secid)) - section['level'] = level if level == 1: if re.match(r'^ACTION', section['title']): self.SECIDS['Action'] = secid @@ -396,9 +394,7 @@ def init_example(self, opt, desc): Returns: dict: New example dict with keys ``opt``, ``title``, and ``desc``. """ - example = {'opt' : opt, 'title' : "", 'desc' : desc.title() + "\n"} - - return example + return {'opt' : opt, 'title' : "", 'desc' : desc.title() + "\n"} # # write the entry file: index.rst @@ -470,32 +466,28 @@ def template_to_rst(self, template, hash, extra=None): if extra is None: extra = "" rstfile = "{}/{}{}.rst".format(self.DOCS['DOCDIR'], template, extra) - tf = open(tempfile, 'r') - rf = open(rstfile, 'w') - idx = 0 - line = tf.readline() - while line: - idx += 1 - if re.match(r'\s*#', line): - line = tf.readline() - continue # skip comment lines - ms = re.match(r'^(.*\S)\s+#', line) - if ms: - line = ms.group(1) # remove comments - else: - line = line.rstrip() # remove trailing white spaces - - matches = re.findall(r'__([A-Z]+)__', line) - if matches: - for key in matches: - if key not in hash: self.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), self.LGWNEX) - if not hash[key]: self.pglog(key + ": empty content", self.LGWNEX) - line = line.replace("__{}__".format(key), hash[key]) - rf.write(line + "\n") + with open(tempfile, 'r') as tf, open(rstfile, 'w') as rf: + idx = 0 line = tf.readline() - - tf.close() - rf.close() + while line: + idx += 1 + if re.match(r'\s*#', line): + line = tf.readline() + continue # skip comment lines + ms = re.match(r'^(.*\S)\s+#', line) + if ms: + line = ms.group(1) # remove comments + else: + line = line.rstrip() # remove trailing white spaces + + matches = re.findall(r'__([A-Z]+)__', line) + if matches: + for key in matches: + if key not in hash: self.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), self.LGWNEX) + if not hash[key]: self.pglog(key + ": empty content", self.LGWNEX) + line = line.replace("__{}__".format(key), hash[key]) + rf.write(line + "\n") + line = tf.readline() self.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), self.LOGWRN) # @@ -684,12 +676,10 @@ def replace_option_link(self, line, csecid, ptype=None, dtype=None): elif ptype == 2: opts = re.findall(r'(-\(*)([a-zA-Z]{2,})(\W|$)', line) ms = re.match(r'^\s*%s(\s+[\w\.]+\s+|\s+)([a-zA-Z]{2})(\s)' % self.DOCS['DOCNAM'], line) - # list.insert() returns None; prepend the match groups explicitly. if ms: opts = [ms.groups()] + opts else: opts = re.findall(r'(^-\(*|\W-\(*)([a-zA-Z]{2,})(\W|$)', line) - if opts is None: opts = [] for optary in opts: opt = self.get_short_option(optary[1]) pre = optary[0] @@ -780,7 +770,7 @@ def create_description(self, desc, secid, dtype): ptype = 0 # paragraph type: 0 - normal, 1 - table, 2 = synopsis content = '' cnt = 0 - alllines = re.split(r'\n', desc) + alllines = desc.split('\n') lines = [] for line in alllines: if re.match(r'^\s*\S', line): @@ -1077,7 +1067,7 @@ def get_short_option(self, p): for opt in self.ALIAS: for alias in self.ALIAS[opt]: - if re.match(r'^{}$'.format(alias), p, re.I): return opt + if re.match(r'^{}$'.format(alias), p, re.I): return opt self.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), self.LGWNEX) @@ -1195,7 +1185,6 @@ def load_opts_alias(self, docname): def main(): """Entry point for command-line usage of pg_rst.py.""" - import argparse parser = argparse.ArgumentParser( description=( "Convert a .usg help document to reStructuredText (.rst) using RST templates. " From 093dc703c2b5060d38afaa79f4417a05b0e505b3 Mon Sep 17 00:00:00 2001 From: zaihuaji Date: Wed, 25 Mar 2026 08:53:41 -0500 Subject: [PATCH 6/6] Replace HTML syntax with RST in pg_rst.py - replace_option_link: unescape </> back to before returning, so tokens escaped during parse_docs appear correctly in RST output instead of as raw <NAME> strings - replace_option_link: remove dead ! HTML entity escape for ; opts list is pre-computed before the loop so re-matching cannot occur, and ! rendered as literal text in RST anyway - parse_docs docstring: clarify the escape/unescape cycle and correct the token format (<NAME>, not <NAME>) Co-Authored-By: Claude Sonnet 4.6 --- src/rda_python_miscs/pg_rst.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/rda_python_miscs/pg_rst.py b/src/rda_python_miscs/pg_rst.py index 06f7ec7..4ac759a 100755 --- a/src/rda_python_miscs/pg_rst.py +++ b/src/rda_python_miscs/pg_rst.py @@ -168,8 +168,10 @@ def parse_docs(self, docname): Lines beginning with ``#`` are treated as comments and skipped. Inline trailing comments are also stripped. Angle-bracketed uppercase tokens - (e.g. ````) are temporarily escaped to ``<FILENAME>`` - so they are not misidentified as option markers later in processing. + (e.g. ````) are temporarily escaped to ``<FILENAME>`` + so they are not misidentified as option markers (``<:>``, ``<=>``, + ````) later in processing. They are unescaped back to ```` + in :meth:`replace_option_link` before appearing in RST output. Args: docname (str): Short document name used to locate ``/.usg``. @@ -701,7 +703,6 @@ def replace_option_link(self, line, csecid, ptype=None, dtype=None): after = ')' replace = pre + opt + after - if re.search(r'', after): after = after.replace(r'', '<!>') link = "{}`{} <{}>`_{}".format(pre, opt, link, after) line = line.replace(replace, link) @@ -742,6 +743,10 @@ def replace_option_link(self, line, csecid, ptype=None, dtype=None): link = self.Q1 + opt + self.Q2 line = line.replace(replace, link) + # Unescape tokens that were temporarily escaped during + # parsing to avoid confusion with option markers (<:>, <=>, ). + line = line.replace('<', '<').replace('>', '>') + return line #