Skip to content

Commit

Permalink
Merge pull request #273 from altendky/less_mockery
Browse files Browse the repository at this point in the history
refactor: configuration I/O to the outside
  • Loading branch information
altendky authored May 8, 2021
2 parents 6763bf3 + 2c2563a commit c4e9551
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 41 deletions.
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ test =
check-manifest
pytest
pytest-cov
pytest-mock
pyfakefs

[options.data_files]
Expand Down
45 changes: 17 additions & 28 deletions src/plotman/_tests/configuration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,60 +8,49 @@
from plotman import resources as plotman_resources


@pytest.fixture(name='config_path')
def config_fixture(tmp_path):
"""Return direct path to plotman.yaml"""
with importlib.resources.path(plotman_resources, "plotman.yaml") as path:
yield path
@pytest.fixture(name='config_text')
def config_text_fixture():
return importlib.resources.read_text(plotman_resources, "plotman.yaml")


def test_get_validated_configs__default(mocker, config_path):
def test_get_validated_configs__default(config_text):
"""Check that get_validated_configs() works with default/example plotman.yaml file."""
mocker.patch("plotman.configuration.get_path", return_value=config_path)
res = configuration.get_validated_configs()
res = configuration.get_validated_configs(config_text, '')
assert isinstance(res, configuration.PlotmanConfig)


def test_get_validated_configs__malformed(mocker, config_path):
def test_get_validated_configs__malformed(config_text):
"""Check that get_validated_configs() raises exception with invalid plotman.yaml contents."""
mocker.patch("plotman.configuration.get_path", return_value=config_path)
with open(configuration.get_path(), "r") as file:
loaded_yaml = yaml.load(file, Loader=yaml.SafeLoader)
loaded_yaml = yaml.load(config_text, Loader=yaml.SafeLoader)

# Purposefully malform the contents of loaded_yaml by changing tmp from List[str] --> str
loaded_yaml["directories"]["tmp"] = "/mnt/tmp/00"
mocker.patch("yaml.load", return_value=loaded_yaml)
malformed_config_text = yaml.dump(loaded_yaml, Dumper=yaml.SafeDumper)

with pytest.raises(configuration.ConfigurationException) as exc_info:
configuration.get_validated_configs()
configuration.get_validated_configs(malformed_config_text, '/the_path')

assert exc_info.value.args[0] == f"Config file at: '{configuration.get_path()}' is malformed"
assert exc_info.value.args[0] == f"Config file at: '/the_path' is malformed"


def test_get_validated_configs__missing(mocker, config_path):
def test_get_validated_configs__missing():
"""Check that get_validated_configs() raises exception when plotman.yaml does not exist."""
nonexistent_config = config_path.with_name("plotman2.yaml")
mocker.patch("plotman.configuration.get_path", return_value=nonexistent_config)

with pytest.raises(configuration.ConfigurationException) as exc_info:
configuration.get_validated_configs()
configuration.read_configuration_text('/invalid_path')

assert exc_info.value.args[0] == (
f"No 'plotman.yaml' file exists at expected location: '{nonexistent_config}'. To generate "
f"No 'plotman.yaml' file exists at expected location: '/invalid_path'. To generate "
f"default config file, run: 'plotman config generate'"
)


def test_loads_without_user_interface(mocker, config_path, tmp_path):
with open(config_path, "r") as file:
loaded_yaml = yaml.load(file, Loader=yaml.SafeLoader)
def test_loads_without_user_interface(config_text):
loaded_yaml = yaml.load(config_text, Loader=yaml.SafeLoader)

del loaded_yaml["user_interface"]

temporary_configuration_path = tmp_path.joinpath("config.yaml")
temporary_configuration_path.write_text(yaml.safe_dump(loaded_yaml))
stripped_config_text = yaml.dump(loaded_yaml, Dumper=yaml.SafeDumper)

mocker.patch("plotman.configuration.get_path", return_value=temporary_configuration_path)
reloaded_yaml = configuration.get_validated_configs()
reloaded_yaml = configuration.get_validated_configs(stripped_config_text, '')

assert reloaded_yaml.user_interface == configuration.UserInterface()
30 changes: 20 additions & 10 deletions src/plotman/configuration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
from typing import Dict, List, Optional

import appdirs
Expand All @@ -16,24 +17,33 @@ def get_path():
return appdirs.user_config_dir("plotman") + "/plotman.yaml"


def get_validated_configs():
def read_configuration_text(config_path):
try:
with open(config_path, "r") as file:
return file.read()
except FileNotFoundError as e:
raise ConfigurationException(
f"No 'plotman.yaml' file exists at expected location: '{config_path}'. To generate "
f"default config file, run: 'plotman config generate'"
) from e


def get_validated_configs(config_text, config_path):
"""Return a validated instance of PlotmanConfig with data from plotman.yaml
:raises ConfigurationException: Raised when plotman.yaml is either missing or malformed
"""
schema = desert.schema(PlotmanConfig)
config_file_path = get_path()
config_objects = yaml.load(config_text, Loader=yaml.SafeLoader)

try:
with open(config_file_path, "r") as file:
config_file = yaml.load(file, Loader=yaml.SafeLoader)
return schema.load(config_file)
except FileNotFoundError as e:
loaded = schema.load(config_objects)
except marshmallow.exceptions.ValidationError as e:
raise ConfigurationException(
f"No 'plotman.yaml' file exists at expected location: '{config_file_path}'. To generate "
f"default config file, run: 'plotman config generate'"
f"Config file at: '{config_path}' is malformed"
) from e
except marshmallow.exceptions.ValidationError as e:
raise ConfigurationException(f"Config file at: '{config_file_path}' is malformed") from e

return loaded


# Data models used to deserializing/formatting plotman.yaml files.
Expand Down
4 changes: 3 additions & 1 deletion src/plotman/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def archiving_status_msg(configured, active, status):
def curses_main(stdscr):
log = Log()

cfg = configuration.get_validated_configs()
config_path = configuration.get_path()
config_text = configuration.read_configuration_text(config_path)
cfg = configuration.get_validated_configs(config_text, config_path)

plotting_active = True
archiving_configured = cfg.directories.archive is not None
Expand Down
4 changes: 3 additions & 1 deletion src/plotman/plotman.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ def main():
print("No action requested, add 'generate' or 'path'.")
return

cfg = configuration.get_validated_configs()
config_path = configuration.get_path()
config_text = configuration.read_configuration_text(config_path)
cfg = configuration.get_validated_configs(config_text, config_path)

#
# Stay alive, spawning plot jobs
Expand Down

0 comments on commit c4e9551

Please sign in to comment.