diff --git a/core/config.py b/core/config.py index fa718f518..bc0c43af7 100644 --- a/core/config.py +++ b/core/config.py @@ -108,12 +108,16 @@ class Group(Value): defaults: dict, spawner, force_registration: bool=False): - self.defaults = defaults + self._defaults = defaults self.force_registration = force_registration self.spawner = spawner super().__init__(identifiers, {}, self.spawner) + @property + def defaults(self): + return self._defaults.copy() + # noinspection PyTypeChecker def __getattr__(self, item: str) -> Union["Group", Value]: """ @@ -135,14 +139,14 @@ class Group(Value): if is_group: return Group( identifiers=new_identifiers, - defaults=self.defaults[item], + defaults=self._defaults[item], spawner=self.spawner, force_registration=self.force_registration ) elif is_value: return Value( identifiers=new_identifiers, - default_value=self.defaults[item], + default_value=self._defaults[item], spawner=self.spawner ) elif self.force_registration: @@ -174,7 +178,7 @@ class Group(Value): :param str item: See :py:meth:`__getattr__`. """ - default = self.defaults.get(item) + default = self._defaults.get(item) return isinstance(default, dict) def is_value(self, item: str) -> bool: @@ -185,7 +189,7 @@ class Group(Value): See :py:meth:`__getattr__`. """ try: - default = self.defaults[item] + default = self._defaults[item] except KeyError: return False @@ -231,19 +235,42 @@ class Group(Value): This method allows you to get "all" of a particular group of data. It will return the dictionary of all data for a particular Guild/Channel/Role/User/Member etc. + .. note:: + + Any values that have not been set from the registered defaults will have their default values + added to the dictionary that this method returns. + :rtype: dict """ - return await self() + defaults = self.defaults + defaults.update(await self()) + return defaults async def all_from_kind(self) -> dict: """ This method allows you to get all data from all entries in a given Kind. It will return a dictionary of Kind ID's -> data. + .. note:: + + Any values that have not been set from the registered defaults will have their default values + added to the dictionary that this method returns. + + .. important:: + + This method is overridden in :py:meth:`.MemberGroup.all_from_kind` and functions slightly differently. + :rtype: dict """ # noinspection PyTypeChecker - return await self._super_group() + all_from_kind = await self._super_group() + + for k, v in all_from_kind.items(): + defaults = self.defaults + defaults.update(v) + all_from_kind[k] = defaults + + return all_from_kind async def set(self, value): if not isinstance(value, dict): @@ -307,14 +334,29 @@ class MemberGroup(Group): """ Returns a dict of :code:`GUILD_ID -> MEMBER_ID -> data`. + .. note:: + + Any values that have not been set from the registered defaults will have their default values + added to the dictionary that this method returns. + :rtype: dict """ # noinspection PyTypeChecker - return await self._super_group() + return await super().all_from_kind() - async def all(self) -> dict: - # noinspection PyTypeChecker - return await self._guild_group() + async def all_from_kind(self) -> dict: + """ + Returns a dict of all members from the same guild as the given one. + + .. note:: + + Any values that have not been set from the registered defaults will have their default values + added to the dictionary that this method returns. + + :rtype: dict + """ + guild_member = await super().all_from_kind() + return guild_member.get(self.identifiers[-2], {}) class Config: @@ -366,7 +408,11 @@ class Config: self.spawner = driver_spawn self.force_registration = force_registration - self.defaults = defaults or {} + self._defaults = defaults or {} + + @property + def defaults(self): + return self._defaults.copy() @classmethod def get_conf(cls, cog_instance, identifier: int, @@ -425,7 +471,7 @@ class Config: def _get_defaults_dict(key: str, value) -> dict: """ Since we're allowing nested config stuff now, not storing the - defaults as a flat dict sounds like a good idea. May turn + _defaults as a flat dict sounds like a good idea. May turn out to be an awful one but we'll see. :param key: :param value: @@ -447,7 +493,7 @@ class Config: @staticmethod def _update_defaults(to_add: dict, _partial: dict): """ - This tries to update the defaults dictionary with the nested + This tries to update the _defaults dictionary with the nested partial dict generated by _get_defaults_dict. This WILL throw an error if you try to have both a value and a group registered under the same name. @@ -471,14 +517,14 @@ class Config: _partial[k] = v def _register_default(self, key: str, **kwargs): - if key not in self.defaults: - self.defaults[key] = {} + if key not in self._defaults: + self._defaults[key] = {} data = deepcopy(kwargs) for k, v in data.items(): to_add = self._get_defaults_dict(k, v) - self._update_defaults(to_add, self.defaults[key]) + self._update_defaults(to_add, self._defaults[key]) def register_global(self, **kwargs): """ @@ -497,7 +543,7 @@ class Config: You can also now register nested values:: - defaults = { + _defaults = { "foo": { "bar": True, "baz": False @@ -506,10 +552,10 @@ class Config: # Will register `foo.bar` == True and `foo.baz` == False conf.register_global( - **defaults + **_defaults ) - You can do the same thing without a :python:`defaults` dict by using double underscore as a variable + You can do the same thing without a :python:`_defaults` dict by using double underscore as a variable name separator:: # This is equivalent to the previous example @@ -556,7 +602,7 @@ class Config: # noinspection PyTypeChecker return group_class( identifiers=(self.unique_identifier, key) + identifiers, - defaults=self.defaults.get(key, {}), + defaults=self._defaults.get(key, {}), spawner=self.spawner, force_registration=self.force_registration ) diff --git a/docs/framework_config.rst b/docs/framework_config.rst index bee691096..ac475b043 100644 --- a/docs/framework_config.rst +++ b/docs/framework_config.rst @@ -43,16 +43,28 @@ API Reference .. automodule:: core.config +Config +^^^^^^ + .. autoclass:: Config :members: +Group +^^^^^ + .. autoclass:: Group :members: :special-members: +MemberGroup +^^^^^^^^^^^ + .. autoclass:: MemberGroup :members: +Value +^^^^^ + .. autoclass:: Value :members: :special-members: __call__ diff --git a/docs/framework_downloader.rst b/docs/framework_downloader.rst index 90bfd7dc2..64c113b17 100644 --- a/docs/framework_downloader.rst +++ b/docs/framework_downloader.rst @@ -69,7 +69,7 @@ Repo Manager :members: Exceptions -^^^^^^ +^^^^^^^^^^ .. automodule:: cogs.downloader.errors :members: diff --git a/tests/conftest.py b/tests/conftest.py index f0ab65b9c..94484fb91 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,7 +37,7 @@ def config(json_driver): unique_identifier=str(uuid.uuid4()), driver_spawn=json_driver) yield conf - conf.defaults = {} + conf._defaults = {} @pytest.fixture() @@ -53,7 +53,7 @@ def config_fr(json_driver): force_registration=True ) yield conf - conf.defaults = {} + conf._defaults = {} #region Dpy Mocks diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 40d859a52..1139ffd69 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -242,7 +242,7 @@ async def test_membergroup_allguilds(config, empty_member): async def test_membergroup_allmembers(config, empty_member): await config.member(empty_member).foo.set(False) - all_members = await config.member(empty_member).all() + all_members = await config.member(empty_member).all_from_kind() assert str(empty_member.id) in all_members @@ -301,6 +301,10 @@ async def test_member_clear_all(config, member_factory): # Get All testing @pytest.mark.asyncio async def test_user_get_all_from_kind(config, user_factory): + config.register_user( + foo=False, + bar=True + ) for _ in range(5): user = user_factory.get() await config.user(user).foo.set(True) @@ -310,10 +314,23 @@ async def test_user_get_all_from_kind(config, user_factory): assert len(all_data) == 5 + for _, v in all_data.items(): + assert v['foo'] is True + assert v['bar'] is True + @pytest.mark.asyncio async def test_user_getalldata(config, user_factory): user = user_factory.get() + config.register_user( + foo=True, + bar=False + ) await config.user(user).foo.set(False) - assert "foo" in await config.user(user).all() + all_data = await config.user(user).all() + + assert "foo" in all_data + assert "bar" in all_data + + assert config.user(user).defaults['foo'] is True