Commit 3ac17e67 authored by Matt Clarkson's avatar Matt Clarkson

Issue #3: Added tests and implementation for ConfigurationManager.

parent 53d18d68
# -*- coding: utf-8 -*-
"""Class to load application configuration information from file."""
Class to load application configuration information from a json file.
Design principles:
- All errors as Exceptions
- | Fail early in constructor, so the rest of the program never
| has an invalid instance of ConfigurationManager.
| If its constructed, its valid.
- Setter and Getter do a deepcopy, so only suitable for small config files.
- | Pass ConfigurationManager to any consumer of the data,
| its up to the consumer to know where to find the data.
import json
import copy
import sksurgerycore.utilities.file_utilities as f
class ConfigurationManager:
Class to load application configuration for example
at startup of an application.
# pylint: disable=line-too-long
""" Class to load application configuration from a json file.
For example, this might be used at the startup of an application.
:param: config_file
:raises: ValueError
:param file_name: a json file to read.
:param write_on_shutdown: if True, will write back to the same file when the destructor is called.
:param write_on_setter: if True, will write back to the same file whenever the setter is called.
:raises: All errors raised as various Exceptions.
def __init__(self, file_name, write_on_shutdown=False):
:params: file_name, a json file to read
:params: write_on_shutdown, if True, will write back to the same
file when the destructor is called.
if not file_name:
raise ValueError("Empty file_name")
def __init__(self, file_name,
""" Constructor. """
if write_on_shutdown or write_on_setter:
with open(file_name, "r") as read_file: = json.load(read_file)
self.file_name = file_name
self.write_on_shutdown = write_on_shutdown
self.write_on_setter = write_on_setter
def __del__(self):
""" If the constructor was passed write_on_shutdown=True,
then the destructor will attempt to save back the config into
the file that it was read from.
if self.write_on_shutdown:
def get_copy(self):
""" Returns a copy of the data read from file.
:returns: deep copy of whatever data structure is stored internally.
return copy.deepcopy(
def set_data(self, data):
""" Stores the provided data internally.
Note that: you would normally load settings from disk,
and then use get_copy() to get a copy, change some settings,
and then use set_data() to pass the data structure back in.
So, the data provided for this method should still represent
the settings you want to save, not just be a completely
arbitrary data structure.
:param data: data structure representing your settings.
if self.write_on_setter:
self._save_back_to_file() = copy.deepcopy(data)
def _save_back_to_file(self):
""" Writes the internal data back to the filename
provided during object construction.
with open(self.file_name, "w") as write_file:
json.dump(, write_file)
# coding=utf-8
Various file utilities.
Various file utilities, often calling standard
functions in the os package, but throwing
nice informative Exception messages.
import os
def validate_is_file(file_name):
Check if file_name exists.
Check if file_name is a file.
if not file_name:
raise ValueError("Empty parameter file_name")
if os.path.isfile(file_name):
return True
raise ValueError('File `' + file_name + '` does not exist')
raise ValueError('File `' + file_name + '` is not a file')
def validate_is_writable_file(file_name):
Check if file_name is a writable file.
if os.access(file_name, os.W_OK):
return True
raise ValueError('File `' + file_name + '` is not a writeable file.')
......@@ -7,9 +7,32 @@ import sksurgerycore.configuration.configuration_manager as cm
def test_constructor_with_empty_filename():
with pytest.raises(ValueError):
manager = cm.ConfigurationManager("")
def test_constructor_with_valid_file():
manager = cm.ConfigurationManager("tests/data/FordPrefect.json")
assert manager is not None
def test_constructor_with_valid_read_only_file():
manager = cm.ConfigurationManager("tests/data/Unwritable.json", False)
assert manager is not None
def test_constructor_fails_if_file_should_be_writable():
with pytest.raises(ValueError):
cm.ConfigurationManager("tests/data/Unwritable.json", True)
def test_setter_getter_loop():
manager = cm.ConfigurationManager("tests/data/FordPrefect.json")
d = manager.get_copy()
d["researcher"]["name"] = "Ford Anglia"
e = manager.get_copy()
assert e["researcher"]["name"] == "Ford Anglia"
"researcher": {
"name": "Ford Prefect",
"species": "Betelgeusian",
"relatives": [
"name": "Zaphod Beeblebrox",
"species": "Betelgeusian"
......@@ -14,7 +14,25 @@ def test_invalid_file_name():
def test_invalid_file_name_because_directory():
with pytest.raises(ValueError):
def test_valid_file_name():
result = f.validate_is_file("tests/data/FordPrefect.json")
assert result
def test_valid_writeable_file():
result = f.validate_is_writable_file("tests/data/FordPrefect.json")
assert result
def test_valid_unwriteable_file():
with pytest.raises(ValueError):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment