Skip to content
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

DM-38694: Add StorageClassDelegate.copy() method #819

Merged
merged 2 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/changes/DM-38694.api.rst
@@ -0,0 +1,2 @@
Added ``StorageClassDelegate.copy()`` method.
By default this method calls `copy.deepcopy()` but subclasses can override as needed.
38 changes: 38 additions & 0 deletions python/lsst/daf/butler/core/storageClassDelegate.py
Expand Up @@ -26,10 +26,13 @@
__all__ = ("DatasetComponent", "StorageClassDelegate")

import collections.abc
import copy
import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, Iterable, Mapping, Optional, Set, Tuple, Type

from lsst.utils.introspection import get_full_type_name

if TYPE_CHECKING:
from .storageClass import StorageClass

Expand Down Expand Up @@ -396,3 +399,38 @@ def selectResponsibleComponent(cls, derivedComponent: str, fromComponents: Set[O
from the supplied options.
"""
raise NotImplementedError("This delegate does not support derived components")

def copy(self, inMemoryDataset: Any) -> Any:
"""Copy the supplied python type and return the copy.

Parameters
----------
inMemoryDataset : `object`
Object to copy.

Returns
-------
copied : `object`
A copy of the supplied object. Can be the same object if the
object is known to be read-only.

Raises
------
NotImplementedError
Raised if none of the default methods for copying work.

Notes
-----
The default implementation uses `copy.deepcopy()`.
It is generally expected that this method is the equivalent of a deep
copy. Subclasses can override this method if they already know the
optimal approach for deep copying.
"""

try:
return copy.deepcopy(inMemoryDataset)
except Exception as e:
raise NotImplementedError(
timj marked this conversation as resolved.
Show resolved Hide resolved
f"Unable to deep copy the supplied python type ({get_full_type_name(inMemoryDataset)}) "
f"using default methods ({e})"
)
15 changes: 15 additions & 0 deletions tests/test_storageClass.py
Expand Up @@ -52,6 +52,11 @@ class PythonType3:
pass


class NotCopyable:
def __deepcopy__(self, memo=None):
raise RuntimeError("Can not be copied.")


class StorageClassFactoryTestCase(unittest.TestCase):
"""Tests of the storage class infrastructure."""

Expand Down Expand Up @@ -92,6 +97,16 @@ def testCreation(self):
# Ensure that we have a delegate
self.assertIsInstance(scc.delegate(), StorageClassDelegate)

# Check that delegate copy() works.
list1 = [1, 2, 3]
list2 = scc.delegate().copy(list1)
self.assertEqual(list1, list2)
list2.append(4)
self.assertNotEqual(list1, list2)

with self.assertRaises(NotImplementedError):
scc.delegate().copy(NotCopyable())

# Check we can create a storageClass using the name of an importable
# type.
sc2 = StorageClass("TestImage2", "lsst.daf.butler.core.storageClass.StorageClassFactory")
Expand Down