Skip to content

Commit

Permalink
Set logger spec_version so links will be to correct spec
Browse files Browse the repository at this point in the history
  • Loading branch information
zimeon committed Oct 24, 2024
1 parent 1bee8e6 commit ece3a0b
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 14 deletions.
2 changes: 2 additions & 0 deletions ocfl/inventory_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def validate(self, inventory, force_spec_version=None):
# Basic structure
self.inventory = inventory
self.spec_version = self.default_spec_version if force_spec_version is None else force_spec_version
self.log.spec_version = self.spec_version
if 'id' in inventory:
iid = inventory['id']
if not isinstance(iid, str) or iid == '':
Expand All @@ -103,6 +104,7 @@ def validate(self, inventory, force_spec_version=None):
self.error('E038b', got=inventory['type'], assumed_spec_version=self.spec_version)
elif m.group(1) in self.spec_versions_supported:
self.spec_version = m.group(1)
self.log.spec_version = self.spec_version
else:
self.error("E038c", got=m.group(1), assumed_spec_version=self.spec_version)
if 'digestAlgorithm' not in inventory:
Expand Down
1 change: 1 addition & 0 deletions ocfl/storage_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ def validate(self, *, validate_objects=True, check_digests=True,
valid = False
self.structure_error = str(e)
logging.debug("Storage root structure is INVALID (%s)", str(e))
self.log.spec_version = self.spec_version
self.num_objects, self.good_objects, self.errors = self.validate_hierarchy(validate_objects=validate_objects, check_digests=check_digests, log_warnings=log_warnings, max_errors=max_errors)
if self.num_traversal_errors > 0:
valid = False
Expand Down
39 changes: 31 additions & 8 deletions ocfl/validation_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,44 @@ class ValidationLogger():
validation_codes = None

def __init__(self, log_warnings=False, log_errors=True,
lang='en', validation_codes=None):
"""Initialize OCFL validation logger."""
spec_version="1.1", lang="en", validation_codes=None):
"""Initialize OCFL validation logger.
Arguments:
log_warnings (bool): True to log warnings via the
warning() method. Default False.
log_errors (bool): True to logs errors via the error()
method. Default True.
spec_version (str): Specification version being validated
against, default "1.1".
lang (str): Language code to look up description strings
with, default "en".
validation_codes (dict): Default None. Usual behavior is to
not use this argument in which case the validation codes
and description data are loaded from the normal location
on first use of this class. Subsequent instantiations use
the same class data. Allows an override to supply the
data explicitly.
"""
self.log_warnings = log_warnings
self.log_errors = log_errors
self.spec_version = spec_version
self.lang = lang
self.codes = {}
self.messages = []
self.num_errors = 0
self.num_warnings = 0
self.spec = 'https://ocfl.io/1.0/spec/'
if validation_codes is not None:
self.validation_codes = validation_codes
elif self.validation_codes is None:
with open(os.path.join(os.path.dirname(__file__), 'data/validation-errors.json'), 'r', encoding="utf-8") as fh:
self.validation_codes = json.load(fh)
# Instance variables to accumulate log data
self.codes = {}
self.messages = []
self.num_errors = 0
self.num_warnings = 0

@property
def spec_url(self):
"""Link to the relevant specification version."""
return "https://ocfl.io/" + self.spec_version + "/spec/"

def log(self, code, is_error, **args):
"""Log either an error or a warning.
Expand Down Expand Up @@ -100,7 +123,7 @@ def log(self, code, is_error, **args):
# Add link to spec
m = re.match(r'''([EW](\d\d\d))''', code)
if m and int(m.group(2)) < 200:
message += ' (see ' + self.spec + '#' + m.group(1) + ')'
message += ' (see ' + self.spec_url + '#' + m.group(1) + ')'
# Store set of codes with last message for that code, and _full_ list of messages
self.codes[code] = message
if (is_error and self.log_errors) or (not is_error and self.log_warnings):
Expand Down
5 changes: 4 additions & 1 deletion ocfl/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def __init__(self, *, log_warnings=False, log_errors=True,
default_spec_version: string of default specification version to
assume where not specified (default '1.1')
log: None (default) to create new ValidationLogger instance, or
else an instance to use.
else use the specified instance which is the appropriate case
for validation of multiple objects within a storage root.
lang: language string (default 'en') to pass to the validation
logger.
"""
Expand Down Expand Up @@ -86,6 +87,7 @@ def initialize(self):
"""
self.id = None
self.spec_version = self.default_spec_version
self.log.spec_version = self.spec_version
self.digest_algorithm = 'sha512'
self.content_directory = 'content'
self.inventory_digest_files = {} # index by version_dir, algorithms may differ
Expand Down Expand Up @@ -142,6 +144,7 @@ def validate_object(self, path):
self.log.error('E003c', assumed_version=self.spec_version)
else:
self.spec_version = spec_version
self.log.spec_version = self.spec_version
if len(namastes) > 1:
self.log.error('E003b', files=len(namastes), using_version=self.spec_version)
# Object root inventory file
Expand Down
24 changes: 19 additions & 5 deletions tests/test_demo_ocfl_validate_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,30 @@ def test01_good(self):

def test02_warnings(self):
"""Test warning cases."""
out = self.run_script("Warning test with -q",
out = self.run_script("Warning test for a v1.0 object",
["python", "ocfl-validate.py",
"-q", "fixtures/1.0/warn-objects/W004_uses_sha256"])
"fixtures/1.0/warn-objects/W004_uses_sha256"],
text="The test shows warning W004 with a link to the v1.0 specification")
self.assertIn("fixtures/1.0/warn-objects/W004_uses_sha256 is VALID", out)
self.assertNotIn("[W004]", out)
out = self.run_script("Warning test without -q",
self.assertIn("[W004]", out)
self.assertIn("https://ocfl.io/1.0/spec/#W004", out)
#
out = self.run_script("Warning test with -q (--quiet) flag",
["python", "ocfl-validate.py",
"fixtures/1.0/warn-objects/W004_uses_sha256"])
"-q", "fixtures/1.0/warn-objects/W004_uses_sha256"],
text="The -q or --quiet flag will silence any warning messages")
self.assertIn("fixtures/1.0/warn-objects/W004_uses_sha256 is VALID", out)
self.assertNotIn("[W004]", out)
#
out = self.run_script("Warning test for a v1.1 object with several warnings",
["python", "ocfl-validate.py",
"fixtures/1.1/warn-objects/W001_W004_W005_zero_padded_versions"],
text="The test shows warning W004 with a link to the v1.0 specification")
self.assertIn("fixtures/1.1/warn-objects/W001_W004_W005_zero_padded_versions is VALID", out)
self.assertIn("[W001]", out)
self.assertIn("[W004]", out)
self.assertIn("[W005]", out)
self.assertIn("https://ocfl.io/1.1/spec/#W001", out)


if __name__ == "__main__":
Expand Down

0 comments on commit ece3a0b

Please sign in to comment.