From 4afbb6859cdbeee9a193fdb6e6624bb56c188575 Mon Sep 17 00:00:00 2001 From: Wolfgang Fahl Date: Thu, 25 Jan 2024 12:35:22 +0100 Subject: [PATCH] tries fixing tests --- lodstorage/linkml_gen.py | 81 +++++++++++++++++++++++----------------- tests/test_samples.py | 11 +++++- tests/test_yamlable.py | 40 +++----------------- 3 files changed, 63 insertions(+), 69 deletions(-) diff --git a/lodstorage/linkml_gen.py b/lodstorage/linkml_gen.py index 56c0055..5edb3e5 100644 --- a/lodstorage/linkml_gen.py +++ b/lodstorage/linkml_gen.py @@ -86,46 +86,59 @@ def gen_schema(self, data_model_instance) -> Schema: """ # Use DocstringParser to extract class description and attributes parser = DocstringParser() - class_description, attributes = parser.parse(data_model_instance.__doc__) + class_description, doc_attributes = parser.parse(data_model_instance.__doc__) - # Add class description to the schema class_name = data_model_instance.__class__.__name__ new_class = Class(description=class_description, slots=[]) - # Add attributes to the schema for field_info in fields(data_model_instance): attr_name = field_info.name attr_type = field_info.type - # Get the value of the attribute from the instance - value = getattr(data_model_instance, attr_name) - # - multivalued = isinstance(value, (Iterable, Mapping)) - if multivalued: - if isinstance(value, Mapping): - value_list = list(value.values()) - else: - value_list = value - if len(value_list) > 0: - value_element = value_list[0] - if is_dataclass(value_element): - linkml_range = type(value_element).__name__ - self.gen_schema(value_element) - pass - else: - linkml_range = self.python_to_linkml_ranges.get( - type(value_element) - ) - - attr_data = attributes.get(attr_name) - new_class.slots.append(attr_name) - if attr_data: - description = attr_data.get("description") - else: - f"{attr_name} - missing description" - new_slot = Slot( - description=description, range=linkml_range, multivalued=multivalued - ) - self.schema.slots[attr_name] = new_slot - self.schema.classes[class_name] = new_class + # Extract field type/range + linkml_range = self.get_linkml_range(attr_type) + + # Check values for multivalued and type consistency + attr_value = getattr(data_model_instance, attr_name) + multivalued, actual_type = self.check_value(attr_value) + + # Ensure documentation, declaration, and value type are consistent + self.ensure_consistency(attr_name, linkml_range, actual_type, doc_attributes) + + # Prepare slot + description = doc_attributes.get(attr_name, {}).get("description", f"{attr_name} - missing description") + if attr_name not in self.schema.slots: + new_slot = Slot(description=description, range=linkml_range, multivalued=multivalued) + self.schema.slots[attr_name] = new_slot + new_class.slots.append(attr_name) + + self.schema.classes[class_name] = new_class return self.schema + + def get_linkml_range(self, attr_type): + # Method to determine the LinkML range from attribute type + return self.python_to_linkml_ranges.get(attr_type, "string") + + def check_value(self, value): + # Method to check if the value is multivalued and determine its type + multivalued = isinstance(value, (Iterable, Mapping)) and not isinstance(value, (str, bytes)) + value_type = type(value).__name__ + return multivalued, value_type + + def ensure_consistency(self, name, declared_type, actual_type, doc_attributes): + # Adjust this method to handle complex types like list, dict, etc. + + # Check if the actual type is a list or dict, and if so, get the type of its elements + if actual_type == 'list' or actual_type == 'dict': + # You may need a more complex logic here to handle lists of custom dataclasses + # For simplicity, let's assume it's a list of strings for now + actual_type = 'string' + + # Now compare the adjusted actual type with the declared type + if declared_type != actual_type: + raise ValueError(f"Type mismatch for '{name}': declared as '{declared_type}', actual type is '{actual_type}'") + + # Check for documentation + if name not in doc_attributes: + raise ValueError(f"Missing documentation for field '{name}'") + diff --git a/tests/test_samples.py b/tests/test_samples.py index 26aadb6..26a16b0 100644 --- a/tests/test_samples.py +++ b/tests/test_samples.py @@ -13,7 +13,7 @@ class TestSamples(Basetest): test the samples """ - def setUp(self, debug=True, profile=True): + def setUp(self, debug=False, profile=True): Basetest.setUp(self, debug=debug, profile=profile) self.sample_cases= [ ("royals",Royals), @@ -170,3 +170,12 @@ def test_json_file_operations(self): # Load from JSON file loaded_item = sample_class.load_from_json_file(filename) self.check_sample(sample_class, sample_name, name, loaded_item) + + def test_load_from_yaml_url(self) -> None: + """ + Test loading a dataclass instance from a YAML string obtained from a URL. + """ + # royals + royals_url = "https://raw.githubusercontent.com/WolfgangFahl/pyLoDStorage/master/sampledata/royals.yaml" + royals = Royals.load_from_yaml_url(royals_url) + self.check_sample(Royals,"royals","QE2 heirs up to number in line 5",royals) diff --git a/tests/test_yamlable.py b/tests/test_yamlable.py index 2c2ee66..a64462e 100644 --- a/tests/test_yamlable.py +++ b/tests/test_yamlable.py @@ -11,13 +11,12 @@ import tempfile from dataclasses import dataclass -from lodstorage.sample2 import Royals, Sample -from lodstorage.yamlable import YamlAble +from lodstorage.yamlable import YamlAble,lod_storable from tests.basetest import Basetest -@dataclass -class MockDataClass(YamlAble["MockDataClass"]): +@lod_storable +class MockDataClass: """ Mock dataclass for testing YAML conversion. @@ -32,7 +31,8 @@ class MockDataClass(YamlAble["MockDataClass"]): id: int description: str = None url: str = None - sample_tuple: tuple = (1, 2, 3) # Add a tuple attribute for testing + flag: bool = True + #sample_tuple: tuple = (1, 2, 3) # Add a tuple attribute for testing class TestYamlAble(Basetest): @@ -110,26 +110,7 @@ def test_block_scalar(self) -> None: "The description should be included as a block scalar.", ) - def test_tuple_serialization(self) -> None: - """ - Test that tuples in the dataclass are serialized as regular YAML lists. - """ - self.mock_data.sample_tuple = (1, 2, 3) - yaml_str = self.mock_data.to_yaml() - debug = self.debug - if debug: - print(yaml_str) - self.assertIn( - "- 1\n- 2\n- 3", - yaml_str, - "Tuples should be serialized as regular YAML lists.", - ) - self.assertNotIn( - "!!python/tuple", - yaml_str, - "Python tuple notation should not appear in the YAML output.", - ) - + def test_save_to_yaml_file(self) -> None: """ Test saving a dataclass instance to a YAML file. @@ -155,12 +136,3 @@ def test_load_from_yaml_file(self) -> None: self.assertEqual(loaded_instance.id, self.mock_data.id) # Clean up the temp file os.remove(temp_file.name) - - def test_load_from_yaml_url(self) -> None: - """ - Test loading a dataclass instance from a YAML string obtained from a URL. - """ - # royals - royals_url = "https://raw.githubusercontent.com/WolfgangFahl/pyLoDStorage/master/sampledata/royals.yaml" - royals = Royals.load_from_yaml_url(royals_url) - self.check_royals(royals)