From 698df6729b113913813c575b9758c58d074fbf64 Mon Sep 17 00:00:00 2001 From: Sumit Saraswat Date: Fri, 9 Oct 2026 10:12:41 +0530 Subject: [PATCH 1/3] Fix memory leak: implement __deepcopy__ for ExtraCoords and GlobalCoords --- ndcube/extra_coords/extra_coords.py | 12 ++++++++++++ ndcube/global_coords.py | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ndcube/extra_coords/extra_coords.py b/ndcube/extra_coords/extra_coords.py index bcbf6724d..c5450a18a 100644 --- a/ndcube/extra_coords/extra_coords.py +++ b/ndcube/extra_coords/extra_coords.py @@ -155,6 +155,18 @@ def __init__(self, ndcube=None): # We need a reference to the parent NDCube self._ndcube = ndcube + def __deepcopy__(self, memo): + import copy + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k == '_ndcube': + setattr(result, k, None) + else: + setattr(result, k, copy.deepcopy(v, memo)) + return result + @classmethod def from_lookup_tables(cls, names, pixel_dimensions, lookup_tables, physical_types=None): """ diff --git a/ndcube/global_coords.py b/ndcube/global_coords.py index 3514a1799..fecc08057 100644 --- a/ndcube/global_coords.py +++ b/ndcube/global_coords.py @@ -88,6 +88,18 @@ def __init__(self, ndcube=None): self._ndcube = ndcube self._internal_coords = OrderedDict() + def __deepcopy__(self, memo): + import copy + cls = self.__class__ + result = cls.__new__(cls) + memo[id(self)] = result + for k, v in self.__dict__.items(): + if k == '_ndcube': + setattr(result, k, None) + else: + setattr(result, k, copy.deepcopy(v, memo)) + return result + @staticmethod def _convert_dropped_to_internal(dropped_dimensions): """ From 1f53a4e656897cdedbe6828a53ac0b0400a81afa Mon Sep 17 00:00:00 2001 From: Sumit Saraswat Date: Fri, 9 Oct 2026 10:29:47 +0530 Subject: [PATCH 2/3] style: sort imports and add changelog --- changelog/991.bugfix.rst | 1 + ndcube/extra_coords/extra_coords.py | 3 ++- ndcube/global_coords.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog/991.bugfix.rst diff --git a/changelog/991.bugfix.rst b/changelog/991.bugfix.rst new file mode 100644 index 000000000..2fbe55b51 --- /dev/null +++ b/changelog/991.bugfix.rst @@ -0,0 +1 @@ +Fixed a memory leak when deep-copying an `NDCube` object or its coordinates by properly implementing `__deepcopy__` for `ExtraCoords` and `GlobalCoords`. diff --git a/ndcube/extra_coords/extra_coords.py b/ndcube/extra_coords/extra_coords.py index c5450a18a..c78dd448c 100644 --- a/ndcube/extra_coords/extra_coords.py +++ b/ndcube/extra_coords/extra_coords.py @@ -1,4 +1,5 @@ import abc +import copy from collections.abc import Iterable from functools import partial, reduce from numbers import Integral @@ -156,7 +157,7 @@ def __init__(self, ndcube=None): self._ndcube = ndcube def __deepcopy__(self, memo): - import copy + cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result diff --git a/ndcube/global_coords.py b/ndcube/global_coords.py index fecc08057..bd564eb31 100644 --- a/ndcube/global_coords.py +++ b/ndcube/global_coords.py @@ -89,7 +89,7 @@ def __init__(self, ndcube=None): self._internal_coords = OrderedDict() def __deepcopy__(self, memo): - import copy + cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result From 23694aae6531ae154381576243d268a0476aed28 Mon Sep 17 00:00:00 2001 From: Sumit Saraswat Date: Fri, 9 Oct 2026 11:22:19 +0530 Subject: [PATCH 3/3] docs: fix rst backticks in changelog --- changelog/991.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/991.bugfix.rst b/changelog/991.bugfix.rst index 2fbe55b51..3e21631ef 100644 --- a/changelog/991.bugfix.rst +++ b/changelog/991.bugfix.rst @@ -1 +1 @@ -Fixed a memory leak when deep-copying an `NDCube` object or its coordinates by properly implementing `__deepcopy__` for `ExtraCoords` and `GlobalCoords`. +Fixed a memory leak when deep-copying an ``NDCube`` object or its coordinates by properly implementing ``__deepcopy__`` for ``ExtraCoords`` and ``GlobalCoords``.