Skip to content

Commit

Permalink
tries fixing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
WolfgangFahl committed Jan 25, 2024
1 parent 62bea0f commit 4afbb68
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 69 deletions.
81 changes: 47 additions & 34 deletions lodstorage/linkml_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}'")

11 changes: 10 additions & 1 deletion tests/test_samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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)
40 changes: 6 additions & 34 deletions tests/test_yamlable.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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):
Expand Down Expand Up @@ -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.
Expand All @@ -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)

0 comments on commit 4afbb68

Please sign in to comment.