-
Notifications
You must be signed in to change notification settings - Fork 30
Dedup logic #1304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
bendichter
wants to merge
14
commits into
dev
Choose a base branch
from
dedup-logic
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Dedup logic #1304
Changes from 9 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
1d149ee
Add complex number rejection to convert_dtype and corresponding tests
bendichter 2730c8d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 0200b3d
Add to changelog
bendichter 53d0c5c
Merge branch 'fix-complex-nums' of https://github.com/hdmf-dev/hdmf i…
bendichter caf0183
Add object deduplication support via soft links
bendichter 80ddda5
Update CHANGELOG.md
bendichter 191d9c3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 27047e1
Update src/hdmf/build/objectmapper.py
bendichter fa552f1
Update src/hdmf/build/objectmapper.py
bendichter c7aa697
Update src/hdmf/build/objectmapper.py
bendichter 4528570
Update tests/unit/build_tests/test_convert_dtype.py
bendichter fa3a54e
Delete tests/unit/build_tests/test_complex_protection.py
bendichter 9b71393
Merge branch 'dev' into dedup-logic
bendichter bf23774
ruff fixes
bendichter File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import unittest | ||
| import numpy as np | ||
|
|
||
| from hdmf.build.objectmapper import ObjectMapper | ||
| from hdmf.spec import DatasetSpec | ||
| from hdmf.testing import TestCase | ||
|
|
||
|
|
||
| class TestComplexProtection(TestCase): | ||
| """Test that complex numbers are properly rejected.""" | ||
|
|
||
| def setUp(self): | ||
| self.spec = DatasetSpec('an example dataset', 'float64', name='data') | ||
|
|
||
| def test_single_complex_number(self): | ||
| """Test that a single complex number is rejected.""" | ||
| with self.assertRaises(ValueError) as cm: | ||
| ObjectMapper.convert_dtype(self.spec, 1 + 2j) | ||
| self.assertEqual(str(cm.exception), "Complex numbers are not supported") | ||
|
|
||
| def test_complex_array(self): | ||
| """Test that an array of complex numbers is rejected.""" | ||
| with self.assertRaises(ValueError) as cm: | ||
| ObjectMapper.convert_dtype(self.spec, np.array([1 + 2j, 3 + 4j])) | ||
| self.assertEqual(str(cm.exception), "Complex numbers are not supported") | ||
|
|
||
| def test_complex_in_list(self): | ||
| """Test that a list containing complex numbers is rejected.""" | ||
| with self.assertRaises(ValueError) as cm: | ||
| ObjectMapper.convert_dtype(self.spec, [1.0, 2 + 3j, 4.0]) | ||
| self.assertEqual(str(cm.exception), "Complex numbers are not supported") | ||
|
|
||
| def test_real_array(self): | ||
| """Test that a real array is not rejected.""" | ||
| ret, ret_dtype = ObjectMapper.convert_dtype(self.spec, np.array([1.0, 2.0, 3.0])) | ||
| self.assertIsInstance(ret, np.ndarray) | ||
|
|
||
| def test_real_number(self): | ||
| """Test that a real number is not rejected.""" | ||
| ret, ret_dtype = ObjectMapper.convert_dtype(self.spec, 3.14) | ||
| self.assertIsInstance(ret, np.float64) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| """Tests for the deduplicate_objects functionality in BuildManager""" | ||
|
|
||
| from hdmf.build import GroupBuilder, DatasetBuilder, BuildManager, TypeMap, ObjectMapper | ||
| from hdmf.spec import GroupSpec, AttributeSpec, DatasetSpec, SpecCatalog, SpecNamespace, NamespaceCatalog | ||
| from hdmf.testing import TestCase | ||
| from hdmf.container import Data | ||
|
|
||
| from tests.unit.helpers.utils import Foo, CORE_NAMESPACE | ||
|
|
||
|
|
||
| class FooMapper(ObjectMapper): | ||
| """Maps nested 'attr2' attribute on dataset 'my_data' to Foo.attr2 in constructor and attribute map""" | ||
|
|
||
| def __init__(self, spec): | ||
| super().__init__(spec) | ||
| my_data_spec = spec.get_dataset('my_data') | ||
| self.map_spec('attr2', my_data_spec.get_attribute('attr2')) | ||
|
|
||
|
|
||
| class TestBuildManagerDeduplication(TestCase): | ||
| """Test BuildManager deduplication functionality""" | ||
|
|
||
| def setUp(self): | ||
| self.foo_spec = GroupSpec( | ||
| doc='A test group specification with a data type', | ||
| data_type_def='Foo', | ||
| datasets=[ | ||
| DatasetSpec( | ||
| doc='an example dataset', | ||
| dtype='int', | ||
| name='my_data', | ||
| attributes=[ | ||
| AttributeSpec( | ||
| name='attr2', | ||
| doc='an example integer attribute', | ||
| dtype='int' | ||
| ) | ||
| ] | ||
| ) | ||
| ], | ||
| attributes=[AttributeSpec('attr1', 'an example string attribute', 'text')] | ||
| ) | ||
|
|
||
| self.spec_catalog = SpecCatalog() | ||
| self.spec_catalog.register_spec(self.foo_spec, 'test.yaml') | ||
| self.namespace = SpecNamespace( | ||
| 'a test namespace', | ||
| CORE_NAMESPACE, | ||
| [{'source': 'test.yaml'}], | ||
| version='0.1.0', | ||
| catalog=self.spec_catalog) | ||
| self.namespace_catalog = NamespaceCatalog() | ||
| self.namespace_catalog.add_namespace(CORE_NAMESPACE, self.namespace) | ||
| self.type_map = TypeMap(self.namespace_catalog) | ||
| self.type_map.register_container_type(CORE_NAMESPACE, 'Foo', Foo) | ||
| self.type_map.register_map(Foo, FooMapper) | ||
|
|
||
| def test_default_deduplicate_objects_true(self): | ||
| """Test that deduplicate_objects defaults to True""" | ||
| manager = BuildManager(self.type_map) | ||
| self.assertTrue(manager.deduplicate_objects) | ||
|
|
||
| def test_deduplicate_objects_explicit_true(self): | ||
| """Test that deduplicate_objects can be explicitly set to True""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=True) | ||
| self.assertTrue(manager.deduplicate_objects) | ||
|
|
||
| def test_deduplicate_objects_explicit_false(self): | ||
| """Test that deduplicate_objects can be explicitly set to False""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=False) | ||
| self.assertFalse(manager.deduplicate_objects) | ||
|
|
||
| def test_get_builder_with_deduplication_enabled(self): | ||
| """Test that get_builder returns cached builder when deduplication is enabled""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=True) | ||
|
|
||
| # Create a simple Data container | ||
| container = Data(name="test_data", data=[1, 2, 3]) | ||
|
|
||
| # Create and cache a builder | ||
| builder = DatasetBuilder(name="test_data", data=[1, 2, 3]) | ||
| manager.prebuilt(container, builder) | ||
|
|
||
| # get_builder should return the cached builder | ||
| cached_builder = manager.get_builder(container) | ||
| self.assertIs(cached_builder, builder) | ||
|
|
||
| def test_get_builder_with_deduplication_disabled(self): | ||
| """Test that get_builder returns None when deduplication is disabled""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=False) | ||
|
|
||
| # Create a simple Data container | ||
| container = Data(name="test_data", data=[1, 2, 3]) | ||
|
|
||
| # Create and cache a builder | ||
| builder = DatasetBuilder(name="test_data", data=[1, 2, 3]) | ||
| manager.prebuilt(container, builder) | ||
|
|
||
| # get_builder should return None when deduplication is disabled | ||
| cached_builder = manager.get_builder(container) | ||
| self.assertIsNone(cached_builder) | ||
|
|
||
| def test_build_memoization_with_deduplication_enabled(self): | ||
| """Test that repeated builds return same builder when deduplication is enabled""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=True) | ||
|
|
||
| container_inst = Foo('my_foo', list(range(10)), 'value1', 10) | ||
|
|
||
| # Build twice - should get same builder | ||
| builder1 = manager.build(container_inst) | ||
| builder2 = manager.build(container_inst) | ||
|
|
||
| self.assertIs(builder1, builder2) | ||
|
|
||
| def test_build_no_memoization_with_deduplication_disabled(self): | ||
| """Test that repeated builds create new builders when deduplication is disabled""" | ||
| manager = BuildManager(self.type_map, deduplicate_objects=False) | ||
|
|
||
| container_inst = Foo('my_foo', list(range(10)), 'value1', 10) | ||
|
|
||
| # Build twice - should get different builders | ||
| builder1 = manager.build(container_inst) | ||
| builder2 = manager.build(container_inst) | ||
|
|
||
| self.assertIsNot(builder1, builder2) | ||
| # But they should have the same content | ||
| self.assertDictEqual(builder1, builder2) | ||
|
|
||
| def test_clear_cache_behavior(self): | ||
| """Test that clear_cache works regardless of deduplication setting""" | ||
| # Test with deduplication enabled | ||
| manager_true = BuildManager(self.type_map, deduplicate_objects=True) | ||
| container = Data(name="test_data", data=[1, 2, 3]) | ||
| builder = DatasetBuilder(name="test_data", data=[1, 2, 3]) | ||
| manager_true.prebuilt(container, builder) | ||
|
|
||
| self.assertIs(manager_true.get_builder(container), builder) | ||
| manager_true.clear_cache() | ||
| self.assertIsNone(manager_true.get_builder(container)) | ||
|
|
||
| # Test with deduplication disabled | ||
| manager_false = BuildManager(self.type_map, deduplicate_objects=False) | ||
| manager_false.prebuilt(container, builder) | ||
|
|
||
| # Should return None even before clearing cache | ||
| self.assertIsNone(manager_false.get_builder(container)) | ||
| manager_false.clear_cache() | ||
| self.assertIsNone(manager_false.get_builder(container)) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.