Skip to content

Commit

Permalink
Replace config-reading code with npg-python-lib npg.conf library
Browse files Browse the repository at this point in the history
  • Loading branch information
nerdstrike committed Dec 18, 2024
1 parent 102c98b commit 82259cc
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 93 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ npg_porch_client = "npg_porch_cli.api_cli_user:run"
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.31.0"
npg-python-lib = { git="https://github.com/wtsi-npg/npg-python-lib", tag="0.3.4"}

[tool.poetry.dev-dependencies]
black = "^22.3.0"
Expand Down
94 changes: 24 additions & 70 deletions src/npg_porch_cli/config.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import configparser
import json
import pathlib
from dataclasses import dataclass
from os import R_OK, access
from os.path import isfile

"""Common utility functions for the package."""
from npg.conf import IniData

DEFAULT_CONF_FILE_TYPE = "ini"


def get_config_data(conf_file_path: str, conf_file_section: str = None):
@dataclass(frozen=True, kw_only=True)
class PorchClientConfig:
"""
Parses a configuration file and returns its content.
Suggested config file content for interacting with a Porch server instance
"""

api_url: str
pipeline_name: str
pipeline_uri: str
pipeline_version: str
npg_porch_token: str

Allows for two types of configuration files, 'ini' and 'json'. The type of
the file is determined from the extension of the file name. In case of no
extension an 'ini' type is assumed.

The content of the file is not cached, so subsequent calls to get data from
the same configuration file result in re-reading and re-parsing of the file.
def get_config_data(
conf_file_path: str, conf_file_section: str = "PORCH"
) -> PorchClientConfig:
"""
Parses a configuration file and returns its content.
Args:
Expand All @@ -37,62 +42,11 @@ def get_config_data(conf_file_path: str, conf_file_section: str = None):
The value corresponding to this key is returned.
"""

path = pathlib.Path(conf_file_path)

conf_file_extension = path.suffix
if conf_file_extension:
conf_file_extension = conf_file_extension[1:]
else:
conf_file_extension = DEFAULT_CONF_FILE_TYPE

if conf_file_extension == DEFAULT_CONF_FILE_TYPE:
if not conf_file_section:
raise Exception(
"'conf_file_section' argument is not given, "
"it should be defined for '{DEFAULT_CONF_FILE_TYPE}' "
"configuration file."
)

config = configparser.ConfigParser()
with open(conf_file_path) as cf:
if not cf.readable():
raise Exception(f"{path} has a permissions issue")

config.read_file(cf)

return {i[0]: i[1] for i in config[conf_file_section].items()}

elif conf_file_extension == "json":
conf: dict = json.load(conf_file_path)
if conf_file_section:
if isinstance(conf, dict) is False:
raise Exception(f"{conf_file_path} does not have sections.")
if conf_file_section in conf.keys:
conf = conf[conf_file_section]
else:
raise Exception(
f"{conf_file_path} does not contain {conf_file_section} key"
)

return conf

if isfile(conf_file_path) and access(conf_file_path, R_OK):
porch_conf = IniData(PorchClientConfig).from_file(
conf_file_path, conf_file_section
)
else:
raise Exception(f"Parsing for '{conf_file_extension}' files is not implemented")


@dataclass(frozen=True, kw_only=True)
class PorchClientConfig:
"""
Suggested config file content for interacting with a Porch server instance
"""

api_url: str
pipeline_name: str
pipeline_uri: str
pipeline_version: str
npg_porch_token: str
raise FileNotFoundError(f"{conf_file_path} is not present or cannot be read")

@classmethod
def from_config_file(cls, conf_file_path: str, conf_file_section: str = "PORCH"):
conf = get_config_data(conf_file_path, conf_file_section=conf_file_section)
return cls(**conf)
return porch_conf
28 changes: 5 additions & 23 deletions tests/test_porch_client_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,15 @@
from npg_porch_cli.config import PorchClientConfig, get_config_data


def test_config_file_reading():
conf = get_config_data(
conf_file_path="tests/data/conf.ini", conf_file_section="STUFF"
)

assert conf == {"logging": "INFO"}


def test_conf_obj():
config_obj = PorchClientConfig.from_config_file("tests/data/conf.ini")
config_obj = get_config_data("tests/data/conf.ini")
assert config_obj.pipeline_name == "test_pipeline"
assert config_obj.pipeline_version == "9.9.9"

with raises(KeyError, match="ABSENT"):
PorchClientConfig.from_config_file(
"tests/data/conf.ini", conf_file_section="ABSENT"
)

with raises(FileNotFoundError, match="No such file or directory: 'notafile'"):
PorchClientConfig.from_config_file("notafile", conf_file_section="ABSENT")
assert type(config_obj) is PorchClientConfig

with raises(TypeError, match="unexpected keyword argument 'logging'"):
PorchClientConfig.from_config_file(
"tests/data/conf.ini", conf_file_section="STUFF"
)
with raises(FileNotFoundError, match="notafile is not present or cannot be read"):
get_config_data("notafile", conf_file_section="ABSENT")

with raises(TypeError, match="missing 2 required keyword-only arguments"):
PorchClientConfig.from_config_file(
"tests/data/conf.ini", conf_file_section="PARTIALPORCH"
)
get_config_data("tests/data/conf.ini", conf_file_section="PARTIALPORCH")

0 comments on commit 82259cc

Please sign in to comment.