diff --git a/extra_fixtures/extension_configs/extension/good_param/config.json b/extra_fixtures/extension_configs/extensions/good_param/config.json similarity index 100% rename from extra_fixtures/extension_configs/extension/good_param/config.json rename to extra_fixtures/extension_configs/extensions/good_param/config.json diff --git a/extra_fixtures/extension_configs/extension/no_config/keep b/extra_fixtures/extension_configs/extensions/no_config/keep similarity index 100% rename from extra_fixtures/extension_configs/extension/no_config/keep rename to extra_fixtures/extension_configs/extensions/no_config/keep diff --git a/extra_fixtures/extension_configs/extension/not_json/config.json b/extra_fixtures/extension_configs/extensions/not_json/config.json similarity index 100% rename from extra_fixtures/extension_configs/extension/not_json/config.json rename to extra_fixtures/extension_configs/extensions/not_json/config.json diff --git a/extra_fixtures/extension_configs/extensions/not_json_object/config.json b/extra_fixtures/extension_configs/extensions/not_json_object/config.json new file mode 100644 index 00000000..d70f61cb --- /dev/null +++ b/extra_fixtures/extension_configs/extensions/not_json_object/config.json @@ -0,0 +1,4 @@ +[ + "This is not a JSON object", + "(it is an array, which is not legal for extension config.json)" +] diff --git a/ocfl/layout.py b/ocfl/layout.py index 8cbce0e5..0d500686 100644 --- a/ocfl/layout.py +++ b/ocfl/layout.py @@ -1,8 +1,10 @@ """Handle different storage layouts. -OCFL Storage roots require a deterministic mapping from the object identifiers -to the path within the storage root. This layout must be consistent across all -objects in the storage root. It often includes two components: +OCFL Storage Roots require a deterministic mapping from the object identifiers +to the path within the storage root. (It is not required that one can deduce the +object id from the path, as is the case with a hashed layout for example). The +layout must be consistent across all objects in the storage root. It often +includes two components: 1) A mapping from the identifier to a set of directory names to create a path where objects are somewhatevenly distributed and will not end up with too many @@ -12,7 +14,7 @@ 2) A final directory name that may be a more complete representation of the object id, by typically with at least some cleaning for safety. Some layouts -use just the remainder of a hash however. Obviously algorithms but avoid +use just the remainder of a hash however. Obviously algorithms must avoid collision in this part within a given path. See: https://ocfl.io/1.1/spec/#root-hierarchies @@ -98,13 +100,19 @@ def read_layout_params(self, root_fs=None, params_required=False): root_fs: the storage root fs object params_required: if True then throw exception for params file not present - Returns None + Returns None, sets instance data in accord with the configuration using + the methods in self.PARAMS to parse for each key. + + Raises LayoutException if the config can't be read or if required by + params_required but not present. """ config = None + print("Reading extension config file %s" % (self.config_file)) if root_fs.exists(self.config_file): try: with root_fs.open(self.config_file) as fh: config = json.load(fh) + print("#### " + str(config)) except Exception as e: raise LayoutException("Storage root extension config file %s exists but can't be read/parsed (%s)" % (self.config_file, str(e))) if not isinstance(config, dict): @@ -122,7 +130,7 @@ def check_and_set_layout_params(self, config, require_extension_name=True): require_extension_name: boolean, True by default. If set False then the extensionName paramater is not required - For each parameter that is recognizedm, the appropriate check and set + For each parameter that is recognized, the appropriate check and set method in self.PARAMS is called. The methods set instance attributes. """ # Check the extensionName if required and/or specified @@ -139,6 +147,9 @@ def write_layout_params(self, root_fs=None): """Write the config.json file with layout parameters if need for this layout. Does nothing if there is no config.json content defined for this layout. + + Raises a LayoutException if there is an error trying to write the config.json + file, including if one already exists. """ config = self.config if config is None: diff --git a/requirements.txt b/requirements.txt index e4f45403..f12c9b46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ fs_s3fs >= 1.1.1 pairtree >= 0.8.1 # Required for extras requests >= 2.20.0 +mock >= 5.1.0 diff --git a/setup.py b/setup.py index e73a9bd4..5f261f6a 100644 --- a/setup.py +++ b/setup.py @@ -70,6 +70,9 @@ def run(self): 'fs_s3fs>=1.1.1', 'pairtree>=0.8.1' ], + tests_require=[ + 'mock>=5.1' + ], test_suite="tests", cmdclass={ 'coverage': Coverage diff --git a/tests/test_layout.py b/tests/test_layout.py index d9433f1e..97580653 100644 --- a/tests/test_layout.py +++ b/tests/test_layout.py @@ -44,7 +44,7 @@ def test_encodea_and_decode(self): self.assertEqual(layout.decode('http%3a%2f%2Fa.b.c'), 'http://a.b.c') self.assertRaises(LayoutException, layout.identifier_to_path, 'id') - def text_read_layout_params(self): + def test_read_layout_params(self): """Test read_layout_params.""" root_fs = open_fs("extra_fixtures/extension_configs") layout = Layout() @@ -54,14 +54,20 @@ def text_read_layout_params(self): def parse_param(value): layout.param = value layout.PARAMS = {"param": parse_param} - layout.read_layout_params(root_fs=root_fs) + layout.read_layout_params(root_fs=root_fs, params_required=True) self.assertEqual(layout.param, "yay!") + # No config file but none required + # Idoesn't do anything, nothing to check) + layout.NAME = "no_config" + layout.read_layout_params(root_fs=root_fs, params_required=False) # Error cases # No config file layout.NAME = "no_config" - self.assertRaises(LayoutException, layout.read_layout_params, root_fs=root_fs) + self.assertRaises(LayoutException, layout.read_layout_params, root_fs=root_fs, params_required=True) layout.NAME = "not_json" self.assertRaises(LayoutException, layout.read_layout_params, root_fs=root_fs) + layout.NAME = "not_json_object" + self.assertRaises(LayoutException, layout.read_layout_params, root_fs=root_fs) def test_check_and_set_layout_params(self): """Test check_and_set_layout_params."""