rda-python-miscs 2.0.2__tar.gz → 2.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. rda_python_miscs-2.0.4/MANIFEST.in +2 -0
  2. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/PKG-INFO +1 -1
  3. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/pyproject.toml +1 -1
  4. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/pg_rst.py +189 -190
  5. rda_python_miscs-2.0.4/src/rda_python_miscs/rst_templates/index.rst.temp +29 -0
  6. rda_python_miscs-2.0.4/src/rda_python_miscs/rst_templates/section.rst.temp +23 -0
  7. rda_python_miscs-2.0.4/src/rda_python_miscs/rst_templates/toc.rst.temp +21 -0
  8. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/PKG-INFO +1 -1
  9. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/SOURCES.txt +3 -0
  10. rda_python_miscs-2.0.2/MANIFEST.in +0 -1
  11. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/LICENSE +0 -0
  12. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/README.md +0 -0
  13. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/setup.cfg +0 -0
  14. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/__init__.py +0 -0
  15. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/bash_qsub.py +0 -0
  16. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/bashqsub.py +0 -0
  17. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/bashqsub.usg +0 -0
  18. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/gdex_ls.py +0 -0
  19. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/gdexls.py +0 -0
  20. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/gdexls.usg +0 -0
  21. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/gdexls_standalone.py +0 -0
  22. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/pg_docs.py +0 -0
  23. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/pg_wget.py +0 -0
  24. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/pgwget.py +0 -0
  25. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_cp.py +0 -0
  26. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_kill.py +0 -0
  27. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_mod.py +0 -0
  28. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_own.py +0 -0
  29. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_ps.py +0 -0
  30. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_sub.py +0 -0
  31. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rda_zip.py +0 -0
  32. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdacp.py +0 -0
  33. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdacp.usg +0 -0
  34. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdakill.py +0 -0
  35. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdakill.usg +0 -0
  36. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdals.py +0 -0
  37. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdals.usg +0 -0
  38. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdamod.py +0 -0
  39. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdamod.usg +0 -0
  40. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdaown.py +0 -0
  41. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdaown.usg +0 -0
  42. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdaps.py +0 -0
  43. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdaps.usg +0 -0
  44. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdasub.py +0 -0
  45. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdasub.usg +0 -0
  46. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdazip.py +0 -0
  47. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/rdazip.usg +0 -0
  48. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/tcsh_qsub.py +0 -0
  49. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/tcshqsub.py +0 -0
  50. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs/tcshqsub.usg +0 -0
  51. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/dependency_links.txt +0 -0
  52. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/entry_points.txt +0 -0
  53. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/requires.txt +0 -0
  54. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/src/rda_python_miscs.egg-info/top_level.txt +0 -0
  55. {rda_python_miscs-2.0.2 → rda_python_miscs-2.0.4}/tests/test_miscs.py +0 -0
@@ -0,0 +1,2 @@
1
+ include src/rda_python_miscs/*.usg
2
+ include src/rda_python_miscs/rst_templates/*.temp
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_miscs
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: RDA Python package to hold RDA miscellaneous utility programs
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-miscs
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "rda_python_miscs"
9
- version = "2.0.2"
9
+ version = "2.0.4"
10
10
  authors = [
11
11
  { name="Zaihua Ji", email="zji@ucar.edu" },
12
12
  ]
@@ -19,7 +19,6 @@ import inspect
19
19
  import argparse
20
20
  import importlib
21
21
  from os import path as op
22
- import PgLOG
23
22
  from rda_python_common.pg_file import PgFile
24
23
  from rda_python_common.pg_util import PgUtil
25
24
 
@@ -142,17 +141,17 @@ class PgRST(PgFile, PgUtil):
142
141
  self.ALIAS = alias
143
142
 
144
143
  self.parse_docs(docname)
145
- if not self.sections: PgLOG.pglog(docname + ": empty document", PgLOG.LGWNEX)
144
+ if not self.sections: self.pglog(docname + ": empty document", self.LGWNEX)
146
145
 
147
146
  self.DOCS['DOCNAM'] = docname
148
147
  if docname in self.LINKS: self.LINKS.remove(docname)
149
148
  self.DOCS['DOCLNK'] = r"({})".format('|'.join(self.LINKS))
150
149
  self.DOCS['DOCTIT'] = docname.upper()
151
- self.change_local_directory(self.DOCS['DOCDIR'], PgLOG.LGWNEX)
152
- PgLOG.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), PgLOG.LOGWRN)
150
+ self.change_local_directory(self.DOCS['DOCDIR'], self.LGWNEX)
151
+ self.pglog("Write rst document '{}' under {}".format(docname, self.DOCS['DOCDIR']), self.LOGWRN)
153
152
 
154
153
  if op.exists("index.rst"): # write index file once
155
- PgLOG.pglog("index.rst exists already, delete first if needs to be regenerated", PgLOG.LOGWRN)
154
+ self.pglog("index.rst exists already, delete first if needs to be regenerated", self.LOGWRN)
156
155
  else:
157
156
  self.write_index(self.sections[0])
158
157
 
@@ -169,69 +168,70 @@ class PgRST(PgFile, PgUtil):
169
168
 
170
169
  Lines beginning with ``#`` are treated as comments and skipped. Inline
171
170
  trailing comments are also stripped. Angle-bracketed uppercase tokens
172
- (e.g. ``<FILENAME>``) are temporarily escaped to ``&lt;FILENAME&gt;``
173
- so they are not misidentified as option markers later in processing.
171
+ (e.g. ``<FILENAME>``) are temporarily escaped to ``&ltFILENAME&gt``
172
+ so they are not misidentified as option markers (``<:>``, ``<=>``,
173
+ ``<!>``) later in processing. They are unescaped back to ``<FILENAME>``
174
+ in :meth:`replace_option_link` before appearing in RST output.
174
175
 
175
176
  Args:
176
177
  docname (str): Short document name used to locate ``<ORIGIN>/<docname>.usg``.
177
178
  """
178
179
  docfile = "{}/{}.usg".format(self.DOCS['ORIGIN'], docname)
179
- PgLOG.pglog("Parsing info for Document '{}'".format(docname), PgLOG.LOGWRN)
180
+ self.pglog("Parsing info for Document '{}'".format(docname), self.LOGWRN)
180
181
  section = self.init_section('0', "Preface")
181
182
  option = example = None
182
- fh = open(docfile, 'r')
183
- line = fh.readline()
184
- while line:
185
- if re.match(r'\s*#', line):
186
- line = fh.readline()
187
- continue # skip comment lines
188
- ms = re.match(r'^(.*\S)\s+#', line)
189
- if ms:
190
- line = ms.group(1) # remove comments
191
- else:
192
- line = line.rstrip() # remove trailing white spaces
193
-
194
- # Temporarily escape <UPPERCASE> tokens so they are not confused
195
- # with special markers like <:>, <=>, <!> used in option parsing.
196
- while True:
197
- ms = re.search(r'(<([A-Z/\-\.]+)>)', line)
183
+ with open(docfile, 'r') as fh:
184
+ line = fh.readline()
185
+ while line:
186
+ if re.match(r'\s*#', line):
187
+ line = fh.readline()
188
+ continue # skip comment lines
189
+ ms = re.match(r'^(.*\S)\s+#', line)
198
190
  if ms:
199
- line = line.replace(ms.group(1), "&lt{}&gt".format(ms.group(2)))
191
+ line = ms.group(1) # remove comments
200
192
  else:
201
- break
202
- ms = re.match(r'^([\d\.]+)\s+(.+)$', line)
203
- if ms: # start new section
204
- section = self.record_section(section, option, example, ms.group(1), ms.group(2))
205
- option = example = None
206
- else:
207
- ms = re.match(r'^ -([A-Z]{2}) or -\w+(.*)$', line)
208
- if ms: # found new option
209
- option = self.record_option(section, option, example, ms.group(1), ms.group(2))
210
- example = None
211
- elif option:
212
- ms = re.match(r'^ For( | another )example, (.*)$', line)
213
- if ms: # found example
214
- example = self.record_example(option, example, ms.group(2))
215
- elif example:
216
- example['desc'] += line + "\n"
193
+ line = line.rstrip() # remove trailing white spaces
194
+
195
+ # Temporarily escape <UPPERCASE> tokens so they are not confused
196
+ # with special markers like <:>, <=>, <!> used in option parsing.
197
+ while True:
198
+ ms = re.search(r'(<([A-Z/\-\.]+)>)', line)
199
+ if ms:
200
+ line = line.replace(ms.group(1), "&lt{}&gt".format(ms.group(2)))
217
201
  else:
218
- option['desc'] += line + "\n"
202
+ break
203
+ ms = re.match(r'^([\d\.]+)\s+(.+)$', line)
204
+ if ms: # start new section
205
+ section = self.record_section(section, option, example, ms.group(1), ms.group(2))
206
+ option = example = None
219
207
  else:
220
- section['desc'] += line + "\n"
208
+ ms = re.match(r'^ -([A-Z]{2}) or -\w+(.*)$', line)
209
+ if ms: # found new option
210
+ option = self.record_option(section, option, example, ms.group(1), ms.group(2))
211
+ example = None
212
+ elif option:
213
+ ms = re.match(r'^ For( | another )example, (.*)$', line)
214
+ if ms: # found example
215
+ example = self.record_example(option, example, ms.group(2))
216
+ elif example:
217
+ example['desc'] += line + "\n"
218
+ else:
219
+ option['desc'] += line + "\n"
220
+ else:
221
+ section['desc'] += line + "\n"
221
222
 
222
- line = fh.readline()
223
- fh.close()
223
+ line = fh.readline()
224
224
 
225
225
  self.record_section(section, option, example)
226
226
 
227
227
  # check completion of options
228
228
  for opt in self.OPTS:
229
229
  if opt not in self.options:
230
- PgLOG.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), PgLOG.LOGWRN)
230
+ self.pglog("Missing option Entry -{} (-{}) in Document '{}'".format(opt, self.OPTS[opt][1], docname), self.LOGWRN)
231
231
  if self.sections:
232
232
  cnt = len(self.sections)
233
233
  s = 's' if cnt > 1 else ''
234
- PgLOG.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), PgLOG.LOGWRN)
234
+ self.pglog("{} Section{} gathered for '{}'".format(cnt, s, docname), self.LOGWRN)
235
235
 
236
236
  #
237
237
  # cache section information
@@ -323,15 +323,14 @@ class PgRST(PgFile, PgUtil):
323
323
  dict: New section dict with keys ``secid``, ``title``, ``desc``,
324
324
  ``level``, and ``opts``.
325
325
  """
326
+ level = secid.count('.') + 1
326
327
  section = {
327
328
  'secid' : secid,
328
329
  'title' : title,
329
330
  'desc' : "",
330
- 'level' : 0,
331
+ 'level' : level,
331
332
  'opts' : []
332
333
  }
333
- level = len(re.split(r'\.', secid))
334
- section['level'] = level
335
334
  if level == 1:
336
335
  if re.match(r'^ACTION', section['title']):
337
336
  self.SECIDS['Action'] = secid
@@ -369,7 +368,7 @@ class PgRST(PgFile, PgUtil):
369
368
  types = ("Mode", "Info", "Info", "Action")
370
369
 
371
370
  if opt not in self.OPTS:
372
- PgLOG.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), PgLOG.LGWNEX)
371
+ self.pglog("{} -- option not defined for {}".format(opt, self.DOCS['DOCNAM']), self.LGWNEX)
373
372
  option['secid'] = secid
374
373
  option['opt'] = opt
375
374
  ms = re.match(r'^(, | \(Alias: .*\), )(.*)', desc)
@@ -397,9 +396,7 @@ class PgRST(PgFile, PgUtil):
397
396
  Returns:
398
397
  dict: New example dict with keys ``opt``, ``title``, and ``desc``.
399
398
  """
400
- example = {'opt' : opt, 'title' : "", 'desc' : desc.title() + "\n"}
401
-
402
- return example
399
+ return {'opt' : opt, 'title' : "", 'desc' : desc.title() + "\n"}
403
400
 
404
401
  #
405
402
  # write the entry file: index.rst
@@ -471,33 +468,29 @@ class PgRST(PgFile, PgUtil):
471
468
  if extra is None: extra = ""
472
469
  rstfile = "{}/{}{}.rst".format(self.DOCS['DOCDIR'], template, extra)
473
470
 
474
- tf = open(tempfile, 'r')
475
- rf = open(rstfile, 'w')
476
- idx = 0
477
- line = tf.readline()
478
- while line:
479
- idx += 1
480
- if re.match(r'\s*#', line):
481
- line = tf.readline()
482
- continue # skip comment lines
483
- ms = re.match(r'^(.*\S)\s+#', line)
484
- if ms:
485
- line = ms.group(1) # remove comments
486
- else:
487
- line = line.rstrip() # remove trailing white spaces
488
-
489
- matches = re.findall(r'__([A-Z]+)__', line)
490
- if matches:
491
- for key in matches:
492
- if key not in hash: PgLOG.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), PgLOG.LGWNEX)
493
- if not hash[key]: PgLOG.pglog(key + ": empty content", PgLOG.LGWNEX)
494
- line = line.replace("__{}__".format(key), hash[key])
495
- rf.write(line + "\n")
471
+ with open(tempfile, 'r') as tf, open(rstfile, 'w') as rf:
472
+ idx = 0
496
473
  line = tf.readline()
497
-
498
- tf.close()
499
- rf.close()
500
- PgLOG.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), PgLOG.LOGWRN)
474
+ while line:
475
+ idx += 1
476
+ if re.match(r'\s*#', line):
477
+ line = tf.readline()
478
+ continue # skip comment lines
479
+ ms = re.match(r'^(.*\S)\s+#', line)
480
+ if ms:
481
+ line = ms.group(1) # remove comments
482
+ else:
483
+ line = line.rstrip() # remove trailing white spaces
484
+
485
+ matches = re.findall(r'__([A-Z]+)__', line)
486
+ if matches:
487
+ for key in matches:
488
+ if key not in hash: self.pglog("{}: not defined at {}({}) {}".format(key, line, idx, tempfile), self.LGWNEX)
489
+ if not hash[key]: self.pglog(key + ": empty content", self.LGWNEX)
490
+ line = line.replace("__{}__".format(key), hash[key])
491
+ rf.write(line + "\n")
492
+ line = tf.readline()
493
+ self.pglog("{}{}.rst created from {}.rst.temp".format(template, extra, template), self.LOGWRN)
501
494
 
502
495
  #
503
496
  # create rst content for table of contents
@@ -685,12 +678,10 @@ class PgRST(PgFile, PgUtil):
685
678
  elif ptype == 2:
686
679
  opts = re.findall(r'(-\(*)([a-zA-Z]{2,})(\W|$)', line)
687
680
  ms = re.match(r'^\s*%s(\s+[\w\.]+\s+|\s+)([a-zA-Z]{2})(\s)' % self.DOCS['DOCNAM'], line)
688
- # list.insert() returns None; prepend the match groups explicitly.
689
681
  if ms: opts = [ms.groups()] + opts
690
682
  else:
691
683
  opts = re.findall(r'(^-\(*|\W-\(*)([a-zA-Z]{2,})(\W|$)', line)
692
684
 
693
- if opts is None: opts = []
694
685
  for optary in opts:
695
686
  opt = self.get_short_option(optary[1])
696
687
  pre = optary[0]
@@ -712,7 +703,6 @@ class PgRST(PgFile, PgUtil):
712
703
  after = ')'
713
704
 
714
705
  replace = pre + opt + after
715
- if re.search(r'<!>', after): after = after.replace(r'<!>', '<&#33;>')
716
706
  link = "{}`{} <{}>`_{}".format(pre, opt, link, after)
717
707
  line = line.replace(replace, link)
718
708
 
@@ -753,6 +743,10 @@ class PgRST(PgFile, PgUtil):
753
743
  link = self.Q1 + opt + self.Q2
754
744
  line = line.replace(replace, link)
755
745
 
746
+ # Unescape <UPPERCASE> tokens that were temporarily escaped during
747
+ # parsing to avoid confusion with option markers (<:>, <=>, <!>).
748
+ line = line.replace('&lt', '<').replace('&gt', '>')
749
+
756
750
  return line
757
751
 
758
752
  #
@@ -778,10 +772,10 @@ class PgRST(PgFile, PgUtil):
778
772
  str: RST-formatted description content.
779
773
  """
780
774
  if desc == "\n": return ''
781
- ptype = 0 # paragraph type: 0 - normal, 1 - table, 2 - synopsis
775
+ ptype = 0 # paragraph type: 0 - normal, 1 - table, 2 = synopsis
782
776
  content = ''
783
777
  cnt = 0
784
- alllines = re.split(r'\n', desc)
778
+ alllines = desc.split('\n')
785
779
  lines = []
786
780
  for line in alllines:
787
781
  if re.match(r'^\s*\S', line):
@@ -1078,9 +1072,9 @@ class PgRST(PgFile, PgUtil):
1078
1072
 
1079
1073
  for opt in self.ALIAS:
1080
1074
  for alias in self.ALIAS[opt]:
1081
- if re.match(r'^{}$'.format(alias), p, re.I): return opt
1075
+ if re.match(r'^{}$'.format(alias), p, re.I): return opt
1082
1076
 
1083
- PgLOG.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), PgLOG.LGWNEX)
1077
+ self.pglog("{} - unknown option for {}".format(p, self.DOCS['DOCNAM']), self.LGWNEX)
1084
1078
 
1085
1079
  #
1086
1080
  # replace with rst link for a given section title
@@ -1118,118 +1112,123 @@ class PgRST(PgFile, PgUtil):
1118
1112
  for section in self.sections:
1119
1113
  if section['secid'] == secid: return section
1120
1114
 
1121
- PgLOG.pglog("Unknown Section ID {}".format(secid), PgLOG.LGWNEX)
1115
+ self.pglog("Unknown Section ID {}".format(secid), self.LGWNEX)
1122
1116
 
1117
+ def load_opts_alias(self, docname):
1118
+ """Import ``rda_python_<docname>.<docname>`` and return its ``(OPTS, ALIAS, origin)`` triple.
1123
1119
 
1124
- # ---------------------------------------------------------------------------
1125
- # Command-line entry point
1126
- # ---------------------------------------------------------------------------
1120
+ Resolution order for OPTS / ALIAS:
1127
1121
 
1128
- def _load_opts_alias(docname):
1129
- """Import ``rda_python_<docname>.<docname>`` and return its ``(OPTS, ALIAS, origin)`` triple.
1122
+ 1. The first class *defined in that module* that carries ``OPTS``
1123
+ as a class-level attribute.
1124
+ 2. Module-level ``OPTS`` / ``ALIAS`` attributes (fallback when no
1125
+ qualifying class is found).
1130
1126
 
1131
- Resolution order for OPTS / ALIAS:
1127
+ ``ALIAS`` is optional; an empty dict is returned when not found.
1128
+
1129
+ The ``origin`` value is the absolute path of the directory that contains
1130
+ ``<docname>.py`` (i.e. ``rda_python_<docname>/``), derived from
1131
+ ``mod.__file__``. It is intended to be assigned to
1132
+ ``PgRST.DOCS['ORIGIN']`` so that :meth:`PgRST.parse_docs` looks for the
1133
+ ``.usg`` source file in the same location as the document module.
1134
+
1135
+ Args:
1136
+ docname (str): Short document name used to build the module path
1137
+ ``rda_python_<docname>.<docname>``.
1138
+
1139
+ Returns:
1140
+ tuple[dict, dict, str]: ``(OPTS, ALIAS, origin)`` where *origin* is
1141
+ the absolute directory path of the imported module file.
1132
1142
 
1133
- 1. Module-level ``OPTS`` / ``ALIAS`` attributes.
1134
- 2. The first class *defined in that module* that carries both ``OPTS``
1135
- and ``ALIAS`` as class-level attributes.
1143
+ Raises:
1144
+ SystemExit: via :func:`PgLOG.pglog` (``LGWNEX``) if the module
1145
+ cannot be imported or ``OPTS`` cannot be found.
1146
+ """
1147
+ modname = "rda_python_{}.{}".format(docname, docname)
1148
+ try:
1149
+ mod = importlib.import_module(modname)
1150
+ except ImportError as exc:
1151
+ self.pglog(
1152
+ "Cannot import module '{}': {}".format(modname, exc),
1153
+ self.LGWNEX,
1154
+ )
1155
+
1156
+ # Derive ORIGIN from the module's own file path.
1157
+ origin = op.dirname(op.abspath(mod.__file__))
1158
+
1159
+ # 1. Find the first class defined in this module and read OPTS / ALIAS from it.
1160
+ cls = next(
1161
+ (obj for _, obj in inspect.getmembers(mod, inspect.isclass)
1162
+ if obj.__module__ == modname),
1163
+ None,
1164
+ )
1136
1165
 
1137
- ``ALIAS`` is optional; an empty dict is returned when not found.
1166
+ if cls is not None:
1167
+ obj = cls()
1168
+ opts = getattr(obj, 'OPTS', None)
1169
+ alias = getattr(obj, 'ALIAS', None)
1170
+ else:
1171
+ # 2. Fall back to module-level attributes when no class is found.
1172
+ opts = getattr(mod, 'OPTS', None)
1173
+ alias = getattr(mod, 'ALIAS', None)
1138
1174
 
1139
- The ``origin`` value is the absolute path of the directory that contains
1140
- ``<docname>.py`` (i.e. ``rda_python_<docname>/``), derived from
1141
- ``mod.__file__``. It is intended to be assigned to
1142
- ``PgRST.DOCS['ORIGIN']`` so that :meth:`PgRST.parse_docs` looks for the
1143
- ``.usg`` source file in the same location as the document module.
1175
+ if opts is None:
1176
+ self.pglog(
1177
+ "Module '{}' does not define OPTS (checked class and "
1178
+ "module level)".format(modname),
1179
+ self.LGWNEX,
1180
+ )
1144
1181
 
1145
- Args:
1146
- docname (str): Short document name used to build the module path
1147
- ``rda_python_<docname>.<docname>``.
1182
+ # ALIAS is optional; default to empty dict.
1183
+ if alias is None:
1184
+ alias = {}
1148
1185
 
1149
- Returns:
1150
- tuple[dict, dict, str]: ``(OPTS, ALIAS, origin)`` where *origin* is
1151
- the absolute directory path of the imported module file.
1186
+ return opts, alias, origin
1152
1187
 
1153
- Raises:
1154
- SystemExit: via :func:`PgLOG.pglog` (``LGWNEX``) if the module
1155
- cannot be imported or ``OPTS`` cannot be found.
1156
- """
1157
- modname = "rda_python_{}.{}".format(docname, docname)
1158
- try:
1159
- mod = importlib.import_module(modname)
1160
- except ImportError as exc:
1161
- PgLOG.pglog(
1162
- "Cannot import module '{}': {}".format(modname, exc),
1163
- PgLOG.LGWNEX,
1164
- )
1165
1188
 
1166
- # Derive ORIGIN from the module's own file path.
1167
- origin = op.dirname(op.abspath(mod.__file__))
1168
-
1169
- # 1. Try module-level attributes first.
1170
- opts = getattr(mod, 'OPTS', None)
1171
- alias = getattr(mod, 'ALIAS', None)
1172
-
1173
- # 2. Fall back to the first class in the module that owns both.
1174
- if opts is None or alias is None:
1175
- for _, obj in inspect.getmembers(mod, inspect.isclass):
1176
- if obj.__module__ == modname:
1177
- cls_opts = getattr(obj, 'OPTS', None)
1178
- cls_alias = getattr(obj, 'ALIAS', None)
1179
- if cls_opts is not None:
1180
- if opts is None: opts = cls_opts
1181
- if alias is None: alias = cls_alias
1182
- break
1183
-
1184
- if opts is None:
1185
- PgLOG.pglog(
1186
- "Module '{}' does not define OPTS (checked module level and "
1187
- "all classes defined in the module)".format(modname),
1188
- PgLOG.LGWNEX,
1189
- )
1189
+ # ---------------------------------------------------------------------------
1190
+ # Command-line entry point
1191
+ # ---------------------------------------------------------------------------
1190
1192
 
1191
- # ALIAS is optional; default to empty dict.
1192
- if alias is None:
1193
- alias = {}
1194
-
1195
- return opts, alias, origin
1196
-
1197
-
1198
- if __name__ == '__main__':
1199
- parser = argparse.ArgumentParser(
1200
- description=(
1201
- "Generate RST documentation from a structured .usg source document.\n\n"
1202
- "OPTS and ALIAS are loaded from rda_python_<docname>/<docname>.py: "
1203
- "the module is searched first for module-level OPTS/ALIAS variables, "
1204
- "then for a class defined in that module that carries both as class "
1205
- "attributes."
1206
- ),
1207
- formatter_class=argparse.RawDescriptionHelpFormatter,
1208
- )
1209
- parser.add_argument(
1210
- 'docname',
1211
- help=(
1212
- "Short document name, e.g. 'dsarch' or 'dsupdt'. "
1213
- "The module rda_python_<docname>/<docname>.py must be importable "
1214
- "and must define OPTS (and optionally ALIAS) either at module "
1215
- "level or as class attributes."
1216
- ),
1217
- )
1218
- parser.add_argument(
1219
- '--docdir',
1220
- default=None,
1221
- metavar='DIR',
1222
- help=(
1223
- "Root directory under which the per-document RST output directory "
1224
- "is created (default: current working directory). "
1225
- "The final output lands in <docdir>/<docname>/."
1226
- ),
1227
- )
1228
- args = parser.parse_args()
1229
-
1230
- opts, alias, origin = _load_opts_alias(args.docname)
1231
- pg = PgRST()
1232
- pg.DOCS['ORIGIN'] = origin
1233
- if args.docdir is not None:
1234
- pg.DOCS['DOCDIR'] = args.docdir
1235
- pg.process_docs(args.docname, opts, alias)
1193
+ def main():
1194
+ """Entry point for command-line usage of pg_rst.py."""
1195
+ parser = argparse.ArgumentParser(
1196
+ description=(
1197
+ "Convert a .usg help document to reStructuredText (.rst) using RST templates. "
1198
+ "OPTS and ALIAS are loaded from rda_python_<docname>/<docname>.py: "
1199
+ "the module is searched first for module-level OPTS/ALIAS variables, "
1200
+ "then for a class defined in that module that carries both as class "
1201
+ "attributes."
1202
+ ),
1203
+ formatter_class=argparse.RawDescriptionHelpFormatter,
1204
+ )
1205
+ parser.add_argument(
1206
+ 'docname',
1207
+ help=(
1208
+ "Short document name, e.g. 'dsarch' or 'dsupdt'. "
1209
+ "The module rda_python_<docname>/<docname>.py must be importable "
1210
+ "and must define OPTS (and optionally ALIAS) either at module "
1211
+ "level or as class attributes."
1212
+ ),
1213
+ )
1214
+ parser.add_argument(
1215
+ '--docdir',
1216
+ default=None,
1217
+ metavar='DIR',
1218
+ help=(
1219
+ "Root directory under which the per-document RST output directory "
1220
+ "is created (default: current working directory). "
1221
+ "The final output lands in <docdir>/<docname>/."
1222
+ ),
1223
+ )
1224
+ args = parser.parse_args()
1225
+
1226
+ pg = PgRST()
1227
+ opts, alias, origin = pg.load_opts_alias(args.docname)
1228
+ pg.DOCS['ORIGIN'] = origin
1229
+ if args.docdir is not None:
1230
+ pg.DOCS['DOCDIR'] = args.docdir
1231
+ pg.process_docs(args.docname, opts, alias)
1232
+
1233
+ if __name__ == "__main__":
1234
+ main()
@@ -0,0 +1,29 @@
1
+ ################################################################################
2
+ #
3
+ # Title : index.temp
4
+ # Author : Zaihua Ji, zji@ucar.edu
5
+ # Date : 03/17/2026
6
+ # Purpose : template file for help document index.rst (reStructuredText)
7
+ #
8
+ # Work File : $DSSHOME/lib/templates/index.temp
9
+ #
10
+ ################################################################################
11
+
12
+ .. _index:
13
+
14
+ ============================
15
+ A GUIDE TO __TITLE__
16
+ ============================
17
+
18
+ .. contents:: Table of Contents
19
+ :depth: 2
20
+ :local:
21
+
22
+ See the :doc:`toc` for the full table of contents.
23
+
24
+ .. toctree::
25
+ :maxdepth: 2
26
+ :caption: Contents
27
+
28
+ toc
29
+ section__SECID__
@@ -0,0 +1,23 @@
1
+ ################################################################################
2
+ #
3
+ # Title : section.temp
4
+ # Author : Zaihua Ji, zji@ucar.edu
5
+ # Date : 03/17/2026
6
+ # Purpose : template file for help document section.rst (reStructuredText)
7
+ #
8
+ # Work File : $DSSHOME/lib/templates/section.temp
9
+ #
10
+ ################################################################################
11
+
12
+ .. _section__SECID__:
13
+
14
+ __SECID__ - __TITLE__
15
+ =====================
16
+
17
+ __SECTION__
18
+
19
+ .. raw:: html
20
+
21
+ <br>
22
+
23
+ :ref:`Back to Top <index>`
@@ -0,0 +1,21 @@
1
+ ################################################################################
2
+ #
3
+ # Title : toc.temp
4
+ # Author : Zaihua Ji, zji@ucar.edu
5
+ # Date : 03/17/2026
6
+ # Purpose : template file for help document toc.rst (table of contents, RST)
7
+ #
8
+ # Work File : $DSSHOME/lib/templates/toc.temp
9
+ #
10
+ ################################################################################
11
+
12
+ .. _toc:
13
+
14
+ ====================
15
+ __TITLE__ Guide
16
+ ====================
17
+
18
+ Table of Contents
19
+ -----------------
20
+
21
+ __TOC__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_miscs
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: RDA Python package to hold RDA miscellaneous utility programs
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-miscs
@@ -46,4 +46,7 @@ src/rda_python_miscs.egg-info/dependency_links.txt
46
46
  src/rda_python_miscs.egg-info/entry_points.txt
47
47
  src/rda_python_miscs.egg-info/requires.txt
48
48
  src/rda_python_miscs.egg-info/top_level.txt
49
+ src/rda_python_miscs/rst_templates/index.rst.temp
50
+ src/rda_python_miscs/rst_templates/section.rst.temp
51
+ src/rda_python_miscs/rst_templates/toc.rst.temp
49
52
  tests/test_miscs.py
@@ -1 +0,0 @@
1
- include src/rda_python_miscs/*.usg