Loading .gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -4,3 +4,4 @@ dist namelist.egg-info *.pyc .coverage .cache examples/merge_NEMO_namelists.py 0 → 100644 +21 −0 Original line number Diff line number Diff line from context import namelist in_file_ref = "namelist_ref" in_file_cfg = "namelist_cfg" with open(in_file_ref) as f: ref_nmls = namelist.parse_namelist_file(f) with open(in_file_cfg) as f: cfg_nmls = namelist.parse_namelist_file(f) nmls = ref_nmls.copy() for k in cfg_nmls: if k in nmls: nmls[k].update(cfg_nmls[k]) else: nmls[k] = cfg_nmls[k] for nml in nmls: print(str(nmls[nml])) namelist/namelist.py +48 −11 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ valueBool = re.compile(r"(\.(true|false|t|f)\.)", re.I) quote = re.compile(r"([\']{1}[^\']*[\']{1}|[\"]{1}[^\"]*[\"]{1})", re.MULTILINE) namelistname = re.compile(r"&(" + varname + r")") paramname = re.compile(r"^(" + varname + r")") paramname = re.compile(r"^(" + varname + r")" + r"(\(([0-9]*)\))?" + r"$") namlistend = re.compile(r'^(&(end)?|/)$', re.I) comment = re.compile(r"!.*$", re.MULTILINE) equalsign = re.compile(r"^=$") Loading Loading @@ -62,8 +62,13 @@ class Namelist(DictClass): """ retstr = "&%s\n" % str(self.name) for k, v in self.items(): if hasattr(v, '__iter__'): retstr += "%s = (/ " % k if hasattr(v, 'islower'): # is a string retstr += "{} = {}\n".format(k, repr(v)) elif hasattr(v, 'items'): # is a dictionary for i, ival in v.items(): retstr += "{}({}) = {}\n".format(k, i, repr(ival)) elif hasattr(v, 'index'): # is an array retstr += "{} = (/ ".format(k) tmpstr = "" for vv in v: if isinstance(vv, bool): Loading Loading @@ -151,53 +156,85 @@ def parse_namelist_string(in_string): """ retlist = [] content = _tokenize(in_string) index = 0 for item in content: match = re.match(namelistname, item) if match: nmlname = match.group(1) nml = Namelist(nmlname) retlist.append(nml) continue match = re.match(paramname, item) if match: pname = match.group(1) index = match.group(3) try: # index assignment index = int(index) if pname not in nml: nml[pname] = DictClass() except TypeError: # index is None nml[pname] = [] continue continue # pragma no cover if re.match(namlistend, item): continue if re.match(equalsign, item): continue match = re.match(valueBool, item) if match: nml[pname].append(match.group(1)[1].lower() == "t") _add_value(nml[pname], match.group(1)[1].lower() == "t", index) continue match = re.match(quote, item) if match: nml[pname].append(match.group(1)[1:-1]) _add_value(nml[pname], match.group(1)[1:-1], index) continue try: nml[pname].append(int(item)) i = int(item) _add_value(nml[pname], i, index) except ValueError: pass else: continue # pragma: no cover try: nml[pname].append(float(item)) f = float(item) _add_value(nml[pname], f, index) except ValueError: pass else: continue # pragma: no cover match = re.match(computation, item) if match: nml[pname].append(eval(item)) _add_value(nml[pname], eval(item), index) for nml in retlist: for k, v in nml.items(): if len(v) == 1: try: nml[k] = v[0] except KeyError: pass ret_dict = DictClass(zip([nml.name for nml in retlist], retlist)) return ret_dict def _add_value(container, value, key): try: # test if container is a dict like object container.keys() container[key] = value except AttributeError: # container is a list container.append(value) def _tokenize(text): """Extract syntax tokens.""" fs = "$$$FS$$$" Loading tests/test_parsing.py +68 −1 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ def test_parse_file(string): "&nml\nval1 = .FALSE.\n/\n", "&nml\nval1 = (/ 1,2,3,4,5,6 /)\n/\n", "&nml\nval1 = (/ .TRUE.,.FALSE.,.TRUE.,.TRUE. /)\n/\n", "&nml\nval1 = 3\nval2 = 5\n/\n", ] ) def test_string_out(string): Loading Loading @@ -189,6 +190,7 @@ def test_var_bool(string, val): ) def test_var_string(string, val): nml = namelist.parse_namelist_string(string)["nml"] print(str(nml)) assert nml["val"] == val Loading @@ -215,6 +217,71 @@ def test_var_array(string, arr): assert type(a) == type(b) @pytest.mark.parametrize("string", ["&nml {} /", "&nml {} val2=34 /"]) @pytest.mark.parametrize("val1", ["lala", "((1 + 2) + 3)", 3, 3.0, 3e6, ".T."]) @pytest.mark.parametrize("d", [{1: None, 2: 35},{1: None}]) def test_index_assignment(string, val1, d): val_str = "" try: int(eval(val1)) is_expression = True except: is_expression = False is_bool = (val1 == ".T.") d[1] = val1 for k, v in d.items(): if is_expression | is_bool: val_str += "val({}) = {} ".format(k, v) else: val_str += "val({}) = {} ".format(k, repr(v)) if is_expression: d[1] = eval(d[1]) if is_bool: d[1] = (val1 == ".T.") nml_string = string.format(val_str) nml = namelist.parse_namelist_string(nml_string)["nml"] print(nml_string) print (repr(nml), d) assert nml["val"] == d @pytest.mark.parametrize("string", ["&nml\n{}/\n", "&nml\n{}val2 = 34\n/\n"]) @pytest.mark.parametrize( "val1", ["some_string", "((1 + 2) + 3)", 3, 3.0, 3e6, ".T."] ) @pytest.mark.parametrize("d", [{1: None, 2: 35}, {1: None}]) def test_index_assignment_to_str(string, d, val1): try: int(eval(val1)) is_expression = True except (NameError, SyntaxError, TypeError): is_expression = False is_bool = (val1 == ".T.") d[1] = val1 val_str = "" for k, v in d.items(): if is_expression | is_bool: val_str += "val({}) = {}\n".format(k, v) else: val_str += "val({}) = {}\n".format(k, repr(v)) nml_string = string.format(val_str) nml = namelist.parse_namelist_string(nml_string)["nml"] if is_expression: d[1] = eval(d[1]) if is_bool: d[1] = (val1 == ".T.") valid_String = "" for k, v in d.items(): valid_String += "val({}) = {}\n".format(k, repr(v)) valid_String = string.format(valid_String) print(valid_String, nml_string, str(nml)) print(is_expression, is_bool) assert valid_String == str(nml) @pytest.mark.parametrize("string", ["&nml val= {}, val2='lsl'/"]) @pytest.mark.parametrize("op", ["+", "-", "/", "*", "**"]) @pytest.mark.parametrize( Loading Loading
.gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -4,3 +4,4 @@ dist namelist.egg-info *.pyc .coverage .cache
examples/merge_NEMO_namelists.py 0 → 100644 +21 −0 Original line number Diff line number Diff line from context import namelist in_file_ref = "namelist_ref" in_file_cfg = "namelist_cfg" with open(in_file_ref) as f: ref_nmls = namelist.parse_namelist_file(f) with open(in_file_cfg) as f: cfg_nmls = namelist.parse_namelist_file(f) nmls = ref_nmls.copy() for k in cfg_nmls: if k in nmls: nmls[k].update(cfg_nmls[k]) else: nmls[k] = cfg_nmls[k] for nml in nmls: print(str(nmls[nml]))
namelist/namelist.py +48 −11 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ valueBool = re.compile(r"(\.(true|false|t|f)\.)", re.I) quote = re.compile(r"([\']{1}[^\']*[\']{1}|[\"]{1}[^\"]*[\"]{1})", re.MULTILINE) namelistname = re.compile(r"&(" + varname + r")") paramname = re.compile(r"^(" + varname + r")") paramname = re.compile(r"^(" + varname + r")" + r"(\(([0-9]*)\))?" + r"$") namlistend = re.compile(r'^(&(end)?|/)$', re.I) comment = re.compile(r"!.*$", re.MULTILINE) equalsign = re.compile(r"^=$") Loading Loading @@ -62,8 +62,13 @@ class Namelist(DictClass): """ retstr = "&%s\n" % str(self.name) for k, v in self.items(): if hasattr(v, '__iter__'): retstr += "%s = (/ " % k if hasattr(v, 'islower'): # is a string retstr += "{} = {}\n".format(k, repr(v)) elif hasattr(v, 'items'): # is a dictionary for i, ival in v.items(): retstr += "{}({}) = {}\n".format(k, i, repr(ival)) elif hasattr(v, 'index'): # is an array retstr += "{} = (/ ".format(k) tmpstr = "" for vv in v: if isinstance(vv, bool): Loading Loading @@ -151,53 +156,85 @@ def parse_namelist_string(in_string): """ retlist = [] content = _tokenize(in_string) index = 0 for item in content: match = re.match(namelistname, item) if match: nmlname = match.group(1) nml = Namelist(nmlname) retlist.append(nml) continue match = re.match(paramname, item) if match: pname = match.group(1) index = match.group(3) try: # index assignment index = int(index) if pname not in nml: nml[pname] = DictClass() except TypeError: # index is None nml[pname] = [] continue continue # pragma no cover if re.match(namlistend, item): continue if re.match(equalsign, item): continue match = re.match(valueBool, item) if match: nml[pname].append(match.group(1)[1].lower() == "t") _add_value(nml[pname], match.group(1)[1].lower() == "t", index) continue match = re.match(quote, item) if match: nml[pname].append(match.group(1)[1:-1]) _add_value(nml[pname], match.group(1)[1:-1], index) continue try: nml[pname].append(int(item)) i = int(item) _add_value(nml[pname], i, index) except ValueError: pass else: continue # pragma: no cover try: nml[pname].append(float(item)) f = float(item) _add_value(nml[pname], f, index) except ValueError: pass else: continue # pragma: no cover match = re.match(computation, item) if match: nml[pname].append(eval(item)) _add_value(nml[pname], eval(item), index) for nml in retlist: for k, v in nml.items(): if len(v) == 1: try: nml[k] = v[0] except KeyError: pass ret_dict = DictClass(zip([nml.name for nml in retlist], retlist)) return ret_dict def _add_value(container, value, key): try: # test if container is a dict like object container.keys() container[key] = value except AttributeError: # container is a list container.append(value) def _tokenize(text): """Extract syntax tokens.""" fs = "$$$FS$$$" Loading
tests/test_parsing.py +68 −1 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ def test_parse_file(string): "&nml\nval1 = .FALSE.\n/\n", "&nml\nval1 = (/ 1,2,3,4,5,6 /)\n/\n", "&nml\nval1 = (/ .TRUE.,.FALSE.,.TRUE.,.TRUE. /)\n/\n", "&nml\nval1 = 3\nval2 = 5\n/\n", ] ) def test_string_out(string): Loading Loading @@ -189,6 +190,7 @@ def test_var_bool(string, val): ) def test_var_string(string, val): nml = namelist.parse_namelist_string(string)["nml"] print(str(nml)) assert nml["val"] == val Loading @@ -215,6 +217,71 @@ def test_var_array(string, arr): assert type(a) == type(b) @pytest.mark.parametrize("string", ["&nml {} /", "&nml {} val2=34 /"]) @pytest.mark.parametrize("val1", ["lala", "((1 + 2) + 3)", 3, 3.0, 3e6, ".T."]) @pytest.mark.parametrize("d", [{1: None, 2: 35},{1: None}]) def test_index_assignment(string, val1, d): val_str = "" try: int(eval(val1)) is_expression = True except: is_expression = False is_bool = (val1 == ".T.") d[1] = val1 for k, v in d.items(): if is_expression | is_bool: val_str += "val({}) = {} ".format(k, v) else: val_str += "val({}) = {} ".format(k, repr(v)) if is_expression: d[1] = eval(d[1]) if is_bool: d[1] = (val1 == ".T.") nml_string = string.format(val_str) nml = namelist.parse_namelist_string(nml_string)["nml"] print(nml_string) print (repr(nml), d) assert nml["val"] == d @pytest.mark.parametrize("string", ["&nml\n{}/\n", "&nml\n{}val2 = 34\n/\n"]) @pytest.mark.parametrize( "val1", ["some_string", "((1 + 2) + 3)", 3, 3.0, 3e6, ".T."] ) @pytest.mark.parametrize("d", [{1: None, 2: 35}, {1: None}]) def test_index_assignment_to_str(string, d, val1): try: int(eval(val1)) is_expression = True except (NameError, SyntaxError, TypeError): is_expression = False is_bool = (val1 == ".T.") d[1] = val1 val_str = "" for k, v in d.items(): if is_expression | is_bool: val_str += "val({}) = {}\n".format(k, v) else: val_str += "val({}) = {}\n".format(k, repr(v)) nml_string = string.format(val_str) nml = namelist.parse_namelist_string(nml_string)["nml"] if is_expression: d[1] = eval(d[1]) if is_bool: d[1] = (val1 == ".T.") valid_String = "" for k, v in d.items(): valid_String += "val({}) = {}\n".format(k, repr(v)) valid_String = string.format(valid_String) print(valid_String, nml_string, str(nml)) print(is_expression, is_bool) assert valid_String == str(nml) @pytest.mark.parametrize("string", ["&nml val= {}, val2='lsl'/"]) @pytest.mark.parametrize("op", ["+", "-", "/", "*", "**"]) @pytest.mark.parametrize( Loading