diff --git a/Orange/data/table.py b/Orange/data/table.py index 260b804a4b9..ab3b7699067 100644 --- a/Orange/data/table.py +++ b/Orange/data/table.py @@ -7,6 +7,7 @@ import zlib from collections.abc import Iterable, Sequence, Sized from contextlib import contextmanager +from copy import deepcopy from functools import reduce from itertools import chain from numbers import Real, Integral @@ -821,7 +822,7 @@ def from_table(cls, domain, source, row_indices=...): self.ids = source.ids[row_indices] else: cls._init_ids(self) - self.attributes = getattr(source, 'attributes', {}) + self.attributes = deepcopy(getattr(source, 'attributes', {})) _idcache_save(_thread_local.conversion_cache, (domain, source), self) return self finally: @@ -879,7 +880,7 @@ def from_table_rows(cls, source, row_indices): self.W = source.W[row_indices] self.name = getattr(source, 'name', '') self.ids = np.array(source.ids[row_indices]) - self.attributes = getattr(source, 'attributes', {}) + self.attributes = deepcopy(getattr(source, 'attributes', {})) return self @classmethod @@ -2284,7 +2285,7 @@ def guessed_var(i, var_name): self.domain = Domain(attributes, class_vars, metas) progress_callback(0.9) cls._init_ids(self) - self.attributes = table.attributes.copy() + self.attributes = deepcopy(table.attributes) self.attributes["old_domain"] = table.domain progress_callback(1) return self diff --git a/Orange/tests/test_table.py b/Orange/tests/test_table.py index bf2ebfead13..e9c4559ed45 100644 --- a/Orange/tests/test_table.py +++ b/Orange/tests/test_table.py @@ -17,7 +17,7 @@ import scipy.sparse as sp from Orange import data -from Orange.data import (filter, Unknown, Variable, Table, DiscreteVariable, +from Orange.data import (filter, Unknown, Table, DiscreteVariable, ContinuousVariable, Domain, StringVariable) from Orange.data.util import SharedComputeValue from Orange.tests import test_dirname @@ -1165,7 +1165,8 @@ def test_attributes(self): table2 = table[:4] self.assertEqual(table2.attributes[1], "test") table2.attributes[1] = "modified" - self.assertEqual(table.attributes[1], "modified") + self.assertEqual(table.attributes[1], "test") + self.assertEqual(table2.attributes[1], "modified") # TODO Test conjunctions and disjunctions of conditions @@ -1893,6 +1894,28 @@ def assert_table_with_filter_matches( np.testing.assert_almost_equal(new_table.metas, magic[rows, mcols]) np.testing.assert_almost_equal(new_table.W, old_table.W[rows]) + def test_attributes_copied(self): + """Table created from table attributes dict copied""" + self.table.attributes = {"A": "Test", "B": []} + + # from_table + new_table = self.table.from_table(self.table.domain, self.table) + self.assertDictEqual(new_table.attributes, {"A": "Test", "B": []}) + new_table.attributes["A"] = "Changed" + new_table.attributes["B"].append(1) + self.assertDictEqual(new_table.attributes, {"A": "Changed", "B": [1]}) + # attributes dict of old table not be changed since new dist is a copy + self.assertDictEqual(self.table.attributes, {"A": "Test", "B": []}) + + # from_table_rows + new_table = self.table.from_table_rows(self.table, [1, 2]) + self.assertDictEqual(new_table.attributes, {"A": "Test", "B": []}) + new_table.attributes["A"] = "Changed" + new_table.attributes["B"].append(1) + self.assertDictEqual(new_table.attributes, {"A": "Changed", "B": [1]}) + # attributes dict of old table not be changed since new dist is a copy + self.assertDictEqual(self.table.attributes, {"A": "Test", "B": []}) + def isspecial(s): return isinstance(s, slice) or s is Ellipsis