diff --git a/redbot/core/config.py b/redbot/core/config.py index 26d47ff15..0eb729aca 100644 --- a/redbot/core/config.py +++ b/redbot/core/config.py @@ -335,7 +335,7 @@ class Group(Value): default = poss_default try: - return deepcopy(await self.driver.get(*self.identifiers, *path)) + return await self.driver.get(*self.identifiers, *path) except KeyError: if default is not ...: return default @@ -365,7 +365,7 @@ class Group(Value): """ if not defaults: - defaults = deepcopy(self.defaults) + defaults = self.defaults for key, value in current.items(): if isinstance(value, collections.Mapping): diff --git a/redbot/core/drivers/red_json.py b/redbot/core/drivers/red_json.py index 7ca1be3aa..0bb4f9ae8 100644 --- a/redbot/core/drivers/red_json.py +++ b/redbot/core/drivers/red_json.py @@ -1,5 +1,6 @@ from pathlib import Path from typing import Tuple +import copy import weakref import logging @@ -97,7 +98,7 @@ class JSON(BaseDriver): full_identifiers = (self.unique_cog_identifier, *identifiers) for i in full_identifiers: partial = partial[i] - return partial + return copy.deepcopy(partial) async def set(self, *identifiers: str, value=None): partial = self.data @@ -107,7 +108,7 @@ class JSON(BaseDriver): partial[i] = {} partial = partial[i] - partial[full_identifiers[-1]] = value + partial[full_identifiers[-1]] = copy.deepcopy(value) await self.jsonIO._threadsafe_save_json(self.data) async def clear(self, *identifiers: str): diff --git a/tests/core/test_config.py b/tests/core/test_config.py index c1d36fed8..b5f232961 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -379,13 +379,9 @@ async def test_value_ctxmgr_saves(config): async def test_value_ctxmgr_immutable(config): config.register_global(foo=True) - try: + with pytest.raises(TypeError): async with config.foo() as foo: foo = False - except TypeError: - pass - else: - raise AssertionError foo = await config.foo() assert foo is True @@ -401,3 +397,25 @@ async def test_ctxmgr_no_shared_default(config, member_factory): foo.append(1) assert 1 not in await config.member(m2).foo() + + +@pytest.mark.asyncio +async def test_get_then_mutate(config): + """Tests that mutating an object after getting it as a value doesn't mutate the data store.""" + config.register_global(list1=[]) + await config.list1.set([]) + list1 = await config.list1() + list1.append("foo") + list1 = await config.list1() + assert "foo" not in list1 + + +@pytest.mark.asyncio +async def test_set_then_mutate(config): + """Tests that mutating an object after setting it as a value doesn't mutate the data store.""" + config.register_global(list1=[]) + list1 = [] + await config.list1.set(list1) + list1.append("foo") + list1 = await config.list1() + assert "foo" not in list1