forked from ESCOMP/CCPPStandardNames
-
Notifications
You must be signed in to change notification settings - Fork 0
/
write_standard_name_table.py
199 lines (189 loc) · 8.46 KB
/
write_standard_name_table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/usr/bin/env python
"""
Convert a metadata standard-name XML library file to a documentation format.
"""
# Python library imports
import xml.etree.ElementTree as ET
import os.path
import argparse
import sys
import re
# Copied from CCPP framework
from xml_tools import validate_xml_file, read_xml_file
from xml_tools import find_schema_file, find_schema_version
_REAL_SUBST_RE = re.compile(r"(.*\d)p(\d.*)")
########################################################################
def standard_name_to_long_name(prop_dict, context=None):
########################################################################
"""Translate a standard_name to its default long_name
Note: This code is copied from the CCPP Framework.
>>> standard_name_to_long_name({'standard_name':'cloud_optical_depth_layers_from_0p55mu_to_0p99mu'})
'Cloud optical depth layers from 0.55mu to 0.99mu'
>>> standard_name_to_long_name({'local_name':'foo'}) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: No standard name to convert foo to long name
>>> standard_name_to_long_name({}) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: No standard name to convert to long name
>>> standard_name_to_long_name({'local_name':'foo'}, context=ParseContext(linenum=3, filename='foo.F90')) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: No standard name to convert foo to long name at foo.F90:3
>>> standard_name_to_long_name({}, context=ParseContext(linenum=3, filename='foo.F90')) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: No standard name to convert to long name at foo.F90:3
"""
# We assume that standar_name has been checked for validity
# Make the first char uppercase and replace each underscore with a space
if 'standard_name' in prop_dict:
standard_name = prop_dict['standard_name']
if standard_name:
long_name = standard_name[0].upper() + re.sub("_", " ",
standard_name[1:])
else:
long_name = ''
# end if
# Next, substitute a decimal point for the p in [:digit]p[:digit]
match = _REAL_SUBST_RE.match(long_name)
while match is not None:
long_name = match.group(1) + '.' + match.group(2)
match = _REAL_SUBST_RE.match(long_name)
# end while
else:
long_name = ''
if 'local_name' in prop_dict:
lname = ' {}'.format(prop_dict['local_name'])
else:
lname = ''
# end if
ctxt = context_string(context)
emsg = 'No standard name to convert{} to long name{}'
raise CCPPError(emsg.format(lname, ctxt))
# end if
return long_name
###############################################################################
def parse_command_line(args, description):
###############################################################################
parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("standard_name_file",
metavar='<standard names filename>',
type=str, help="XML file with standard name library")
parser.add_argument("--output-filename", metavar='<output filename>',
type=str, default='Metadata-standard-names',
help="Name of output file (without extension)")
parser.add_argument("--output-format", metavar='md', type=str, default='md',
help="Format of output file")
pargs = parser.parse_args(args)
return pargs
###############################################################################
def convert_xml_to_markdown(root, library_name, snl):
###############################################################################
snl.write('# {}\n'.format(library_name))
# Write a table of contents
snl.write('#### Table of Contents\n')
for section in root:
sec_name = section.get('name')
snl.write("* [{name}](#{name})\n".format(name=sec_name))
# end for
snl.write('\n')
for section in root:
# Step through the sections
sec_name = section.get('name')
sec_comment = section.get('comment')
snl.write('## {}\n'.format(sec_name))
if sec_comment is not None:
# First, squeeze out the spacing
while sec_comment.find(' ') >= 0:
sec_comment = sec_comment.replace(' ', ' ')
# end while
while sec_comment:
sec_comment = sec_comment.lstrip()
cind = sec_comment.find('\\n')
if cind > 0:
snl.write('{}\n'.format(sec_comment[0:cind]))
sec_comment = sec_comment[cind+2:]
else:
snl.write('{}\n'.format(sec_comment))
sec_comment = ''
# end if
# end while
# end if
for std_name in section:
stdn_name = std_name.get('name')
stdn_longname = std_name.get('long_name')
if stdn_longname is None:
sdict = {'standard_name':stdn_name}
stdn_longname = standard_name_to_long_name(sdict)
# end if
snl.write("* `{}`: {}\n".format(stdn_name, stdn_longname))
# Should only be a type in the standard_name text
for item in std_name:
if item.tag == 'type':
txt = item.text
kind = item.get('kind')
if kind is None:
kstr = ''
else:
kstr = "(kind={})".format(kind)
# end if
units = item.get('units')
snl.write(' * `{}{}`: units = {}\n'.format(txt, kstr,
units))
else:
emsg = "Unknown standard name property, '{}'"
raise ValueError(emsg.format(item.tag))
# end if
# end for
# end for
# end for
###############################################################################
def main_func():
###############################################################################
"""Validate and parse the standard names database file and generate
a document containing the data.
Currently, only the Markdown format is supported for output.
"""
# Parse command line arguments
args = parse_command_line(sys.argv[1:], __doc__)
stdname_file = os.path.abspath(args.standard_name_file)
# Read the XML file
_, root = read_xml_file(stdname_file)
library_name = root.get('name')
# Validate the XML file (needs to be here to grab the version)
version = find_schema_version(root)
schema_name = os.path.basename(stdname_file)[0:-4]
schema_root = os.path.dirname(stdname_file)
schema_file = find_schema_file(schema_name, version)
if not schema_file:
emsg = 'Cannot find schema file, {}, for version {}'
raise ValueError(emsg.format(schema_name, version))
# end if
try:
emsg = "Invalid standard names file, {}".format(stdname_file)
file_ok = validate_xml_file(stdname_file, schema_name, version,
None, schema_path=schema_root,
error_on_noxmllint=True)
except ValueError as valerr:
cemsg = "{}".format(valerr).split('\n')[0]
if cemsg[0:12] == 'Execution of':
xstart = cemsg.find("'")
if xstart >= 0:
xend = cemsg[xstart + 1:].find("'") + xstart + 1
emsg += '\n' + cemsg[xstart + 1:xend]
# end if (else, just keep original message)
elif cemsg[0:18] == 'validate_xml_file:':
emsg += "\n" + cemsg
# end if
raise ValueError(emsg)
# end try
if args.output_format != 'md':
emsg = "Unsupported output format, '{}'"
raise ValueError(emsg.format(args.output_format))
# end if
outfile_name = args.output_filename
with open("{}.{}".format(outfile_name, args.output_format), "w") as snl:
convert_xml_to_markdown(root, library_name, snl)
# end with
###############################################################################
if __name__ == "__main__":
main_func()