Skip to content

Commit

Permalink
Improve config handler, large restructuring and start of testing and …
Browse files Browse the repository at this point in the history
…type annotations (#11)

* move tests into ldndc2nc folder

* parse_config requires a section arg

* add test for find_config and new dev requirement pyfakefs for fs mocking

* update default conf to new multi-var syntax

* update gitignore

* remove superfluous update_docs.sh

* add some special folders/ files to gitignore

* add some special folders/ files to gitignore (2)

* add some special folders/ files to gitignore (3)

* change tests from xfail to raises

* add new config_handler and variable classes and associated tests

* move to config_handler (incomplete)

* make sure we do not drop cols prematurely in case of aggregate cols

* allow nan and 0 as non-id values in reffile

* new id_mapper and various changes to variable and unit handling

* small fix

* disable test for confighandler.file_path momentarily

* add setuptools to gh actions ci

* fix test_find_path for config_handler

* small fix

* disbale config_handler test to see if this makes it pass the github actions ci test step
  • Loading branch information
cwerner authored May 13, 2020
1 parent 287a094 commit 25cbf48
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 86 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
*.py[cod]

*.nc

__pycache__
pip-wheel-metadata
_version.py
.pytest_cache
.DS_Store
.vscode

# C extensions
*.so

Expand Down
143 changes: 143 additions & 0 deletions ldndc2nc/config_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
"""ldndc2nc.config_handler: read the configuration settings for this ldndc2nc run."""

import logging
import os
from pathlib import Path

import yaml

from .variable import Variable

log = logging.getLogger(__name__)


def find_config(local_file=None):
""" look for config file in the default locations """
env_var = os.environ.get("LDNDC2NC_CONF", "__NOTSET__")
locations = [
x / "ldndc2nc.conf" for x in [Path("."), Path.home(), Path("/etc/ldndc2nc")]
]
locations.append(Path(env_var))

if local_file:
locations.insert(0, Path(local_file))

for cfg_file in locations:
if cfg_file.is_file():
return cfg_file
return None


def read_config(file_path) -> None:
""" read yaml config file and modify special properties"""

print(f"read_config: {file_path}")
with open(file_path, "r") as ymlfile:
cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)

return cfg


def write_config(self, dest=Path.home()):
""" write cfg file to user dir """

if self.cfg:
self._decode()
fname = Path(dest) / "ldndc2nc.conf"
with open(fname, "w") as f:
f.write(yaml.dump(self._decode(self.cfg), default_flow_style=False))


def get_section(self, section):
""" parse config data structure, return data of required section """

if self.cfg:
self.cfg = self._encode(self.cfg)

section_data = None

def is_valid_section(s):
valid_sections = ["info", "project", "variables", "refdata"]
return s in valid_sections

if is_valid_section(section.lower()):
try:
section_data = self.cfg.get(section, self.cfg.get(section.lower()))
except KeyError:
log.critical(
f"Section <{section.lower()}> not found in cfg file {self.file_path}"
)
log.critical(
f"The following sections are present: {list(self.cfg.keys())}."
)
else:
log.critical(f"Section {section.lower()} is not a valid section")
raise RuntimeError
return section_data


class ConfigHandler:

_write_config = write_config
_get_section = get_section

def __init__(self, local_file=None):
self.cfg_file = find_config(local_file=local_file)
raw = read_config(self.cfg_file)
self.cfg = self._encode(raw)

@staticmethod
def _decode(cfg):
if "variables" in cfg:
for file, variables in cfg["variables"].items():
cfg["variables"][file] = [v.text_full for v in variables]
return cfg

@staticmethod
def _encode(cfg):
if "variables" in cfg:
for file, variables in cfg["variables"].items():
cfg["variables"][file] = [
Variable(v) if isinstance(v, str) else v for v in variables
]
return cfg

@property
def file_path(self):
if not self.cfg_file:
self.cfg_file = find_config()
return self.cfg_file

@property
def variables(self):
vars = []
if "variables" in self.cfg:
for file, variables in self.cfg["variables"].items():
vars += [Variable(v) if isinstance(v, str) else v for v in variables]
return vars

@property
def text(self):
clean = self._decode(self.cfg)
return yaml.dump(clean, default_style=False) if self.cfg else None

@property
def global_info(self):
global_info = {}
for section_name in ["info", "project"]:
section = self.section(section_name)
if section:
global_info.update(section)
else:
log.warn(f"No <{section_name}> data found in config")
return global_info

def section(self, section):
return self._get_section(section)

def write(self, *args, **kwargs):
self._write_config(*args, **kwargs)

def __repr__(self):
return f"<cfg: {self.file_path}>"
4 changes: 2 additions & 2 deletions ldndc2nc/data/ldndc2nc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ project:
#
variables:
soilchemistry-daily.txt:
- dC_co2_emis[kgCha-1];dC_co2_emis_auto[kgCha-1];dC_co2_emis_hetero[kgCha-1]
- dC_co2_emis[kgCha-1]=dC_co2_emis_auto[kgCha-1]+dC_co2_emis_hetero[kgCha-1]
- dN_n2o_emis[kgNha-1]
- dN_no_emis[kgNha-1]
- dN_n2_emis[kgNha-1]
Expand All @@ -44,7 +44,7 @@ variables:
report-fertilize.txt:
- dN_fertilizer[kgNha-1]
report-harvest.txt:
- dC_habove[kgCha-1];dC_bud[kgCha-1];dC_straw[kgCha-1]
- dC_habove[kgCha-1]=dC_bud[kgCha-1]+dC_straw[kgCha-1]
- dC_bud[kgCha-1]
- dC_stubble[kgCha-1]

Expand Down
26 changes: 11 additions & 15 deletions ldndc2nc/extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,19 @@ def _copy_default_config():
shutil.copyfile(fname, Path.home() / "ldndc2nc.conf")


def _find_config():
def _find_config() -> Path:
""" look for cfgFile in the default locations """
cfgFile = None
env_var = os.environ.get("LDNDC2NC_CONF", "__NOTSET__")

locations = [
Path("."),
Path.home(),
Path("/etc/ldndc2nc"),
os.environ.get("LDNDC2NC_CONF"),
x / "ldndc2nc.conf" for x in [Path("."), Path.home(), Path("/etc/ldndc2nc")]
]
locations = [x for x in locations if x is not None]
locations.append(Path(env_var))

for loc in locations:
f = loc / "ldndc2nc.conf"
if f.is_file(f):
cfgFile = str(f)
break
if loc.is_file():
return loc

return cfgFile

Expand Down Expand Up @@ -72,7 +69,7 @@ def is_multipart_item(x):
return cfg


def parse_config(cfg, section=None):
def parse_config(cfg, section):
""" parse config data structure, return data of required section """

def is_valid_section(s):
Expand Down Expand Up @@ -101,10 +98,9 @@ def get_config(cfgFile=None):
def cfgfile_exists(cfgFile):
return cfgFile is not None

if cfgfile_exists(cfgFile):
if not os.path.isfile(cfgFile):
log.critical("Specified configuration file not found.")
exit(1)
if not Path(cfgFile).is_file():
log.critical(f"Specified config file not found: {cfgFile}")
exit(1)
else:
cfgFile = _find_config()

Expand Down
Loading

0 comments on commit 25cbf48

Please sign in to comment.