We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Config V3] User all returning wrong level
All is currently returning USER_ID -> data instead of just data

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `core/config.py`
Content:
```
1 import logging
2 
3 from typing import Callable, Union, Tuple
4 
5 import discord
6 from copy import deepcopy
7 
8 from pathlib import Path
9 
10 from .drivers.red_json import JSON as JSONDriver
11 
12 log = logging.getLogger("red.config")
13 
14 
15 class Value:
16     def __init__(self, identifiers: Tuple[str], default_value, spawner):
17         self._identifiers = identifiers
18         self.default = default_value
19 
20         self.spawner = spawner
21 
22     @property
23     def identifiers(self):
24         return tuple(str(i) for i in self._identifiers)
25 
26     def __call__(self, default=None):
27         driver = self.spawner.get_driver()
28         try:
29             ret = driver.get(self.identifiers)
30         except KeyError:
31             return default or self.default
32         return ret
33 
34     async def set(self, value):
35         driver = self.spawner.get_driver()
36         await driver.set(self.identifiers, value)
37 
38 
39 class Group(Value):
40     def __init__(self, identifiers: Tuple[str],
41                  defaults: dict,
42                  spawner,
43                  force_registration: bool=False):
44         self.defaults = defaults
45         self.force_registration = force_registration
46         self.spawner = spawner
47 
48         super().__init__(identifiers, {}, self.spawner)
49 
50     # noinspection PyTypeChecker
51     def __getattr__(self, item: str) -> Union["Group", Value]:
52         """
53         Takes in the next accessible item. If it's found to be a Group
54             we return another Group object. If it's found to be a Value
55             we return a Value object. If it is not found and
56             force_registration is True then we raise AttributeException,
57             otherwise return a Value object.
58         :param item:
59         :return:
60         """
61         is_group = self.is_group(item)
62         is_value = not is_group and self.is_value(item)
63         new_identifiers = self.identifiers + (item, )
64         if is_group:
65             return Group(
66                 identifiers=new_identifiers,
67                 defaults=self.defaults[item],
68                 spawner=self.spawner,
69                 force_registration=self.force_registration
70             )
71         elif is_value:
72             return Value(
73                 identifiers=new_identifiers,
74                 default_value=self.defaults[item],
75                 spawner=self.spawner
76             )
77         elif self.force_registration:
78             raise AttributeError(
79                 "'{}' is not a valid registered Group"
80                 "or value.".format(item)
81             )
82         else:
83             return Value(
84                 identifiers=new_identifiers,
85                 default_value=None,
86                 spawner=self.spawner
87             )
88 
89     @property
90     def _super_group(self) -> 'Group':
91         super_group = Group(
92             self.identifiers[:-1],
93             defaults={},
94             spawner=self.spawner,
95             force_registration=self.force_registration
96         )
97         return super_group
98 
99     def is_group(self, item: str) -> bool:
100         """
101         Determines if an attribute access is pointing at a registered group.
102         :param item:
103         :return:
104         """
105         default = self.defaults.get(item)
106         return isinstance(default, dict)
107 
108     def is_value(self, item: str) -> bool:
109         """
110         Determines if an attribute access is pointing at a registered value.
111         :param item:
112         :return:
113         """
114         try:
115             default = self.defaults[item]
116         except KeyError:
117             return False
118 
119         return not isinstance(default, dict)
120 
121     def get_attr(self, item: str, default=None):
122         """
123         You should avoid this function whenever possible.
124         :param item:
125         :param default:
126         :return:
127         """
128         value = getattr(self, item)
129         return value(default=default)
130 
131     def all(self) -> dict:
132         """
133         Gets all entries of the given kind. If this kind is member
134             then this method returns all members from the same
135             server.
136         :return:
137         """
138         # noinspection PyTypeChecker
139         return self._super_group()
140 
141     async def set(self, value):
142         if not isinstance(value, dict):
143             raise ValueError(
144                 "You may only set the value of a group to be a dict."
145             )
146         await super().set(value)
147 
148     async def set_attr(self, item: str, value):
149         """
150         You should avoid this function whenever possible.
151         :param item:
152         :param value:
153         :return:
154         """
155         value_obj = getattr(self, item)
156         await value_obj.set(value)
157 
158     async def clear(self):
159         """
160         Wipes out data for the given entry in this category
161             e.g. Guild/Role/User
162         :return:
163         """
164         await self.set({})
165 
166     async def clear_all(self):
167         """
168         Removes all data from all entries.
169         :return:
170         """
171         await self._super_group.set({})
172 
173 
174 class MemberGroup(Group):
175     @property
176     def _super_group(self) -> Group:
177         new_identifiers = self.identifiers[:2]
178         group_obj = Group(
179             identifiers=new_identifiers,
180             defaults={},
181             spawner=self.spawner
182         )
183         return group_obj
184 
185     @property
186     def _guild_group(self) -> Group:
187         new_identifiers = self.identifiers[:3]
188         group_obj = Group(
189             identifiers=new_identifiers,
190             defaults={},
191             spawner=self.spawner
192         )
193         return group_obj
194 
195     def all_guilds(self) -> dict:
196         """
197         Gets a dict of all guilds and members.
198 
199         REMEMBER: ID's are stored in these dicts as STRINGS.
200         :return:
201         """
202         # noinspection PyTypeChecker
203         return self._super_group()
204 
205     def all(self) -> dict:
206         """
207         Returns the dict of all members in the same guild.
208         :return:
209         """
210         # noinspection PyTypeChecker
211         return self._guild_group()
212 
213 class Config:
214     GLOBAL = "GLOBAL"
215     GUILD = "GUILD"
216     CHANNEL = "TEXTCHANNEL"
217     ROLE = "ROLE"
218     USER = "USER"
219     MEMBER = "MEMBER"
220 
221     def __init__(self, cog_name: str, unique_identifier: str,
222                  driver_spawn: Callable,
223                  force_registration: bool=False,
224                  defaults: dict=None):
225         self.cog_name = cog_name
226         self.unique_identifier = unique_identifier
227 
228         self.spawner = driver_spawn
229         self.force_registration = force_registration
230         self.defaults = defaults or {}
231 
232     @classmethod
233     def get_conf(cls, cog_instance, identifier: int,
234                  force_registration=False):
235         """
236         Returns a Config instance based on a simplified set of initial
237             variables.
238         :param cog_instance:
239         :param identifier: Any random integer, used to keep your data
240             distinct from any other cog with the same name.
241         :param force_registration: Should config require registration
242             of data keys before allowing you to get/set values?
243         :return:
244         """
245         cog_name = cog_instance.__class__.__name__
246         uuid = str(hash(identifier))
247 
248         spawner = JSONDriver(cog_name)
249         return cls(cog_name=cog_name, unique_identifier=uuid,
250                    force_registration=force_registration,
251                    driver_spawn=spawner)
252 
253     @classmethod
254     def get_core_conf(cls, force_registration: bool=False):
255         core_data_path = Path.cwd() / 'core' / '.data'
256         driver_spawn = JSONDriver("Core", data_path_override=core_data_path)
257         return cls(cog_name="Core", driver_spawn=driver_spawn,
258                    unique_identifier='0',
259                    force_registration=force_registration)
260 
261     def __getattr__(self, item: str) -> Union[Group, Value]:
262         """
263         This is used to generate Value or Group objects for global
264             values.
265         :param item:
266         :return:
267         """
268         global_group = self._get_base_group(self.GLOBAL)
269         return getattr(global_group, item)
270 
271     @staticmethod
272     def _get_defaults_dict(key: str, value) -> dict:
273         """
274         Since we're allowing nested config stuff now, not storing the
275             defaults as a flat dict sounds like a good idea. May turn
276             out to be an awful one but we'll see.
277         :param key:
278         :param value:
279         :return:
280         """
281         ret = {}
282         partial = ret
283         splitted = key.split('__')
284         for i, k in enumerate(splitted, start=1):
285             if not k.isidentifier():
286                 raise RuntimeError("'{}' is an invalid config key.".format(k))
287             if i == len(splitted):
288                 partial[k] = value
289             else:
290                 partial[k] = {}
291                 partial = partial[k]
292         return ret
293 
294     @staticmethod
295     def _update_defaults(to_add: dict, _partial: dict):
296         """
297         This tries to update the defaults dictionary with the nested
298             partial dict generated by _get_defaults_dict. This WILL
299             throw an error if you try to have both a value and a group
300             registered under the same name.
301         :param to_add:
302         :param _partial:
303         :return:
304         """
305         for k, v in to_add.items():
306             val_is_dict = isinstance(v, dict)
307             if k in _partial:
308                 existing_is_dict = isinstance(_partial[k], dict)
309                 if val_is_dict != existing_is_dict:
310                     # != is XOR
311                     raise KeyError("You cannot register a Group and a Value under"
312                                    " the same name.")
313                 if val_is_dict:
314                     Config._update_defaults(v, _partial=_partial[k])
315                 else:
316                     _partial[k] = v
317             else:
318                 _partial[k] = v
319 
320     def _register_default(self, key: str, **kwargs):
321         if key not in self.defaults:
322             self.defaults[key] = {}
323 
324         data = deepcopy(kwargs)
325 
326         for k, v in data.items():
327             to_add = self._get_defaults_dict(k, v)
328             self._update_defaults(to_add, self.defaults[key])
329 
330     def register_global(self, **kwargs):
331         self._register_default(self.GLOBAL, **kwargs)
332 
333     def register_guild(self, **kwargs):
334         self._register_default(self.GUILD, **kwargs)
335 
336     def register_channel(self, **kwargs):
337         # We may need to add a voice channel category later
338         self._register_default(self.CHANNEL, **kwargs)
339 
340     def register_role(self, **kwargs):
341         self._register_default(self.ROLE, **kwargs)
342 
343     def register_user(self, **kwargs):
344         self._register_default(self.USER, **kwargs)
345 
346     def register_member(self, **kwargs):
347         self._register_default(self.MEMBER, **kwargs)
348 
349     def _get_base_group(self, key: str, *identifiers: str,
350                         group_class=Group) -> Group:
351         # noinspection PyTypeChecker
352         return group_class(
353             identifiers=(self.unique_identifier, key) + identifiers,
354             defaults=self.defaults.get(key, {}),
355             spawner=self.spawner,
356             force_registration=self.force_registration
357         )
358 
359     def guild(self, guild: discord.Guild) -> Group:
360         return self._get_base_group(self.GUILD, guild.id)
361 
362     def channel(self, channel: discord.TextChannel) -> Group:
363         return self._get_base_group(self.CHANNEL, channel.id)
364 
365     def role(self, role: discord.Role) -> Group:
366         return self._get_base_group(self.ROLE, role.id)
367 
368     def user(self, user: discord.User) -> Group:
369         return self._get_base_group(self.USER, user.id)
370 
371     def member(self, member: discord.Member) -> MemberGroup:
372         return self._get_base_group(self.MEMBER, member.guild.id, member.id,
373                                     group_class=MemberGroup)
374 
375
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/core/config.py b/core/config.py
--- a/core/config.py
+++ b/core/config.py
@@ -118,17 +118,29 @@
 
         return not isinstance(default, dict)
 
-    def get_attr(self, item: str, default=None):
+    def get_attr(self, item: str, default=None, resolve=True):
         """
         You should avoid this function whenever possible.
         :param item:
         :param default:
+        :param resolve:
+            If this is True, actual data will be returned, if false a Group/Value will be returned.
         :return:
         """
         value = getattr(self, item)
-        return value(default=default)
+        if resolve:
+            return value(default=default)
+        else:
+            return value
 
     def all(self) -> dict:
+        """
+        Gets all data from current User/Member/Guild etc.
+        :return:
+        """
+        return self()
+
+    def all_from_kind(self) -> dict:
         """
         Gets all entries of the given kind. If this kind is member
             then this method returns all members from the same

{"golden_diff": "diff --git a/core/config.py b/core/config.py\n--- a/core/config.py\n+++ b/core/config.py\n@@ -118,17 +118,29 @@\n \n         return not isinstance(default, dict)\n \n-    def get_attr(self, item: str, default=None):\n+    def get_attr(self, item: str, default=None, resolve=True):\n         \"\"\"\n         You should avoid this function whenever possible.\n         :param item:\n         :param default:\n+        :param resolve:\n+            If this is True, actual data will be returned, if false a Group/Value will be returned.\n         :return:\n         \"\"\"\n         value = getattr(self, item)\n-        return value(default=default)\n+        if resolve:\n+            return value(default=default)\n+        else:\n+            return value\n \n     def all(self) -> dict:\n+        \"\"\"\n+        Gets all data from current User/Member/Guild etc.\n+        :return:\n+        \"\"\"\n+        return self()\n+\n+    def all_from_kind(self) -> dict:\n         \"\"\"\n         Gets all entries of the given kind. If this kind is member\n             then this method returns all members from the same\n", "issue": "[Config V3] User all returning wrong level\nAll is currently returning USER_ID -> data instead of just data\n", "before_files": [{"content": "import logging\n\nfrom typing import Callable, Union, Tuple\n\nimport discord\nfrom copy import deepcopy\n\nfrom pathlib import Path\n\nfrom .drivers.red_json import JSON as JSONDriver\n\nlog = logging.getLogger(\"red.config\")\n\n\nclass Value:\n    def __init__(self, identifiers: Tuple[str], default_value, spawner):\n        self._identifiers = identifiers\n        self.default = default_value\n\n        self.spawner = spawner\n\n    @property\n    def identifiers(self):\n        return tuple(str(i) for i in self._identifiers)\n\n    def __call__(self, default=None):\n        driver = self.spawner.get_driver()\n        try:\n            ret = driver.get(self.identifiers)\n        except KeyError:\n            return default or self.default\n        return ret\n\n    async def set(self, value):\n        driver = self.spawner.get_driver()\n        await driver.set(self.identifiers, value)\n\n\nclass Group(Value):\n    def __init__(self, identifiers: Tuple[str],\n                 defaults: dict,\n                 spawner,\n                 force_registration: bool=False):\n        self.defaults = defaults\n        self.force_registration = force_registration\n        self.spawner = spawner\n\n        super().__init__(identifiers, {}, self.spawner)\n\n    # noinspection PyTypeChecker\n    def __getattr__(self, item: str) -> Union[\"Group\", Value]:\n        \"\"\"\n        Takes in the next accessible item. If it's found to be a Group\n            we return another Group object. If it's found to be a Value\n            we return a Value object. If it is not found and\n            force_registration is True then we raise AttributeException,\n            otherwise return a Value object.\n        :param item:\n        :return:\n        \"\"\"\n        is_group = self.is_group(item)\n        is_value = not is_group and self.is_value(item)\n        new_identifiers = self.identifiers + (item, )\n        if is_group:\n            return Group(\n                identifiers=new_identifiers,\n                defaults=self.defaults[item],\n                spawner=self.spawner,\n                force_registration=self.force_registration\n            )\n        elif is_value:\n            return Value(\n                identifiers=new_identifiers,\n                default_value=self.defaults[item],\n                spawner=self.spawner\n            )\n        elif self.force_registration:\n            raise AttributeError(\n                \"'{}' is not a valid registered Group\"\n                \"or value.\".format(item)\n            )\n        else:\n            return Value(\n                identifiers=new_identifiers,\n                default_value=None,\n                spawner=self.spawner\n            )\n\n    @property\n    def _super_group(self) -> 'Group':\n        super_group = Group(\n            self.identifiers[:-1],\n            defaults={},\n            spawner=self.spawner,\n            force_registration=self.force_registration\n        )\n        return super_group\n\n    def is_group(self, item: str) -> bool:\n        \"\"\"\n        Determines if an attribute access is pointing at a registered group.\n        :param item:\n        :return:\n        \"\"\"\n        default = self.defaults.get(item)\n        return isinstance(default, dict)\n\n    def is_value(self, item: str) -> bool:\n        \"\"\"\n        Determines if an attribute access is pointing at a registered value.\n        :param item:\n        :return:\n        \"\"\"\n        try:\n            default = self.defaults[item]\n        except KeyError:\n            return False\n\n        return not isinstance(default, dict)\n\n    def get_attr(self, item: str, default=None):\n        \"\"\"\n        You should avoid this function whenever possible.\n        :param item:\n        :param default:\n        :return:\n        \"\"\"\n        value = getattr(self, item)\n        return value(default=default)\n\n    def all(self) -> dict:\n        \"\"\"\n        Gets all entries of the given kind. If this kind is member\n            then this method returns all members from the same\n            server.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._super_group()\n\n    async def set(self, value):\n        if not isinstance(value, dict):\n            raise ValueError(\n                \"You may only set the value of a group to be a dict.\"\n            )\n        await super().set(value)\n\n    async def set_attr(self, item: str, value):\n        \"\"\"\n        You should avoid this function whenever possible.\n        :param item:\n        :param value:\n        :return:\n        \"\"\"\n        value_obj = getattr(self, item)\n        await value_obj.set(value)\n\n    async def clear(self):\n        \"\"\"\n        Wipes out data for the given entry in this category\n            e.g. Guild/Role/User\n        :return:\n        \"\"\"\n        await self.set({})\n\n    async def clear_all(self):\n        \"\"\"\n        Removes all data from all entries.\n        :return:\n        \"\"\"\n        await self._super_group.set({})\n\n\nclass MemberGroup(Group):\n    @property\n    def _super_group(self) -> Group:\n        new_identifiers = self.identifiers[:2]\n        group_obj = Group(\n            identifiers=new_identifiers,\n            defaults={},\n            spawner=self.spawner\n        )\n        return group_obj\n\n    @property\n    def _guild_group(self) -> Group:\n        new_identifiers = self.identifiers[:3]\n        group_obj = Group(\n            identifiers=new_identifiers,\n            defaults={},\n            spawner=self.spawner\n        )\n        return group_obj\n\n    def all_guilds(self) -> dict:\n        \"\"\"\n        Gets a dict of all guilds and members.\n\n        REMEMBER: ID's are stored in these dicts as STRINGS.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._super_group()\n\n    def all(self) -> dict:\n        \"\"\"\n        Returns the dict of all members in the same guild.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._guild_group()\n\nclass Config:\n    GLOBAL = \"GLOBAL\"\n    GUILD = \"GUILD\"\n    CHANNEL = \"TEXTCHANNEL\"\n    ROLE = \"ROLE\"\n    USER = \"USER\"\n    MEMBER = \"MEMBER\"\n\n    def __init__(self, cog_name: str, unique_identifier: str,\n                 driver_spawn: Callable,\n                 force_registration: bool=False,\n                 defaults: dict=None):\n        self.cog_name = cog_name\n        self.unique_identifier = unique_identifier\n\n        self.spawner = driver_spawn\n        self.force_registration = force_registration\n        self.defaults = defaults or {}\n\n    @classmethod\n    def get_conf(cls, cog_instance, identifier: int,\n                 force_registration=False):\n        \"\"\"\n        Returns a Config instance based on a simplified set of initial\n            variables.\n        :param cog_instance:\n        :param identifier: Any random integer, used to keep your data\n            distinct from any other cog with the same name.\n        :param force_registration: Should config require registration\n            of data keys before allowing you to get/set values?\n        :return:\n        \"\"\"\n        cog_name = cog_instance.__class__.__name__\n        uuid = str(hash(identifier))\n\n        spawner = JSONDriver(cog_name)\n        return cls(cog_name=cog_name, unique_identifier=uuid,\n                   force_registration=force_registration,\n                   driver_spawn=spawner)\n\n    @classmethod\n    def get_core_conf(cls, force_registration: bool=False):\n        core_data_path = Path.cwd() / 'core' / '.data'\n        driver_spawn = JSONDriver(\"Core\", data_path_override=core_data_path)\n        return cls(cog_name=\"Core\", driver_spawn=driver_spawn,\n                   unique_identifier='0',\n                   force_registration=force_registration)\n\n    def __getattr__(self, item: str) -> Union[Group, Value]:\n        \"\"\"\n        This is used to generate Value or Group objects for global\n            values.\n        :param item:\n        :return:\n        \"\"\"\n        global_group = self._get_base_group(self.GLOBAL)\n        return getattr(global_group, item)\n\n    @staticmethod\n    def _get_defaults_dict(key: str, value) -> dict:\n        \"\"\"\n        Since we're allowing nested config stuff now, not storing the\n            defaults as a flat dict sounds like a good idea. May turn\n            out to be an awful one but we'll see.\n        :param key:\n        :param value:\n        :return:\n        \"\"\"\n        ret = {}\n        partial = ret\n        splitted = key.split('__')\n        for i, k in enumerate(splitted, start=1):\n            if not k.isidentifier():\n                raise RuntimeError(\"'{}' is an invalid config key.\".format(k))\n            if i == len(splitted):\n                partial[k] = value\n            else:\n                partial[k] = {}\n                partial = partial[k]\n        return ret\n\n    @staticmethod\n    def _update_defaults(to_add: dict, _partial: dict):\n        \"\"\"\n        This tries to update the defaults dictionary with the nested\n            partial dict generated by _get_defaults_dict. This WILL\n            throw an error if you try to have both a value and a group\n            registered under the same name.\n        :param to_add:\n        :param _partial:\n        :return:\n        \"\"\"\n        for k, v in to_add.items():\n            val_is_dict = isinstance(v, dict)\n            if k in _partial:\n                existing_is_dict = isinstance(_partial[k], dict)\n                if val_is_dict != existing_is_dict:\n                    # != is XOR\n                    raise KeyError(\"You cannot register a Group and a Value under\"\n                                   \" the same name.\")\n                if val_is_dict:\n                    Config._update_defaults(v, _partial=_partial[k])\n                else:\n                    _partial[k] = v\n            else:\n                _partial[k] = v\n\n    def _register_default(self, key: str, **kwargs):\n        if key not in self.defaults:\n            self.defaults[key] = {}\n\n        data = deepcopy(kwargs)\n\n        for k, v in data.items():\n            to_add = self._get_defaults_dict(k, v)\n            self._update_defaults(to_add, self.defaults[key])\n\n    def register_global(self, **kwargs):\n        self._register_default(self.GLOBAL, **kwargs)\n\n    def register_guild(self, **kwargs):\n        self._register_default(self.GUILD, **kwargs)\n\n    def register_channel(self, **kwargs):\n        # We may need to add a voice channel category later\n        self._register_default(self.CHANNEL, **kwargs)\n\n    def register_role(self, **kwargs):\n        self._register_default(self.ROLE, **kwargs)\n\n    def register_user(self, **kwargs):\n        self._register_default(self.USER, **kwargs)\n\n    def register_member(self, **kwargs):\n        self._register_default(self.MEMBER, **kwargs)\n\n    def _get_base_group(self, key: str, *identifiers: str,\n                        group_class=Group) -> Group:\n        # noinspection PyTypeChecker\n        return group_class(\n            identifiers=(self.unique_identifier, key) + identifiers,\n            defaults=self.defaults.get(key, {}),\n            spawner=self.spawner,\n            force_registration=self.force_registration\n        )\n\n    def guild(self, guild: discord.Guild) -> Group:\n        return self._get_base_group(self.GUILD, guild.id)\n\n    def channel(self, channel: discord.TextChannel) -> Group:\n        return self._get_base_group(self.CHANNEL, channel.id)\n\n    def role(self, role: discord.Role) -> Group:\n        return self._get_base_group(self.ROLE, role.id)\n\n    def user(self, user: discord.User) -> Group:\n        return self._get_base_group(self.USER, user.id)\n\n    def member(self, member: discord.Member) -> MemberGroup:\n        return self._get_base_group(self.MEMBER, member.guild.id, member.id,\n                                    group_class=MemberGroup)\n\n", "path": "core/config.py"}], "after_files": [{"content": "import logging\n\nfrom typing import Callable, Union, Tuple\n\nimport discord\nfrom copy import deepcopy\n\nfrom pathlib import Path\n\nfrom .drivers.red_json import JSON as JSONDriver\n\nlog = logging.getLogger(\"red.config\")\n\n\nclass Value:\n    def __init__(self, identifiers: Tuple[str], default_value, spawner):\n        self._identifiers = identifiers\n        self.default = default_value\n\n        self.spawner = spawner\n\n    @property\n    def identifiers(self):\n        return tuple(str(i) for i in self._identifiers)\n\n    def __call__(self, default=None):\n        driver = self.spawner.get_driver()\n        try:\n            ret = driver.get(self.identifiers)\n        except KeyError:\n            return default or self.default\n        return ret\n\n    async def set(self, value):\n        driver = self.spawner.get_driver()\n        await driver.set(self.identifiers, value)\n\n\nclass Group(Value):\n    def __init__(self, identifiers: Tuple[str],\n                 defaults: dict,\n                 spawner,\n                 force_registration: bool=False):\n        self.defaults = defaults\n        self.force_registration = force_registration\n        self.spawner = spawner\n\n        super().__init__(identifiers, {}, self.spawner)\n\n    # noinspection PyTypeChecker\n    def __getattr__(self, item: str) -> Union[\"Group\", Value]:\n        \"\"\"\n        Takes in the next accessible item. If it's found to be a Group\n            we return another Group object. If it's found to be a Value\n            we return a Value object. If it is not found and\n            force_registration is True then we raise AttributeException,\n            otherwise return a Value object.\n        :param item:\n        :return:\n        \"\"\"\n        is_group = self.is_group(item)\n        is_value = not is_group and self.is_value(item)\n        new_identifiers = self.identifiers + (item, )\n        if is_group:\n            return Group(\n                identifiers=new_identifiers,\n                defaults=self.defaults[item],\n                spawner=self.spawner,\n                force_registration=self.force_registration\n            )\n        elif is_value:\n            return Value(\n                identifiers=new_identifiers,\n                default_value=self.defaults[item],\n                spawner=self.spawner\n            )\n        elif self.force_registration:\n            raise AttributeError(\n                \"'{}' is not a valid registered Group\"\n                \"or value.\".format(item)\n            )\n        else:\n            return Value(\n                identifiers=new_identifiers,\n                default_value=None,\n                spawner=self.spawner\n            )\n\n    @property\n    def _super_group(self) -> 'Group':\n        super_group = Group(\n            self.identifiers[:-1],\n            defaults={},\n            spawner=self.spawner,\n            force_registration=self.force_registration\n        )\n        return super_group\n\n    def is_group(self, item: str) -> bool:\n        \"\"\"\n        Determines if an attribute access is pointing at a registered group.\n        :param item:\n        :return:\n        \"\"\"\n        default = self.defaults.get(item)\n        return isinstance(default, dict)\n\n    def is_value(self, item: str) -> bool:\n        \"\"\"\n        Determines if an attribute access is pointing at a registered value.\n        :param item:\n        :return:\n        \"\"\"\n        try:\n            default = self.defaults[item]\n        except KeyError:\n            return False\n\n        return not isinstance(default, dict)\n\n    def get_attr(self, item: str, default=None, resolve=True):\n        \"\"\"\n        You should avoid this function whenever possible.\n        :param item:\n        :param default:\n        :param resolve:\n            If this is True, actual data will be returned, if false a Group/Value will be returned.\n        :return:\n        \"\"\"\n        value = getattr(self, item)\n        if resolve:\n            return value(default=default)\n        else:\n            return value\n\n    def all(self) -> dict:\n        \"\"\"\n        Gets all data from current User/Member/Guild etc.\n        :return:\n        \"\"\"\n        return self()\n\n    def all_from_kind(self) -> dict:\n        \"\"\"\n        Gets all entries of the given kind. If this kind is member\n            then this method returns all members from the same\n            server.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._super_group()\n\n    async def set(self, value):\n        if not isinstance(value, dict):\n            raise ValueError(\n                \"You may only set the value of a group to be a dict.\"\n            )\n        await super().set(value)\n\n    async def set_attr(self, item: str, value):\n        \"\"\"\n        You should avoid this function whenever possible.\n        :param item:\n        :param value:\n        :return:\n        \"\"\"\n        value_obj = getattr(self, item)\n        await value_obj.set(value)\n\n    async def clear(self):\n        \"\"\"\n        Wipes out data for the given entry in this category\n            e.g. Guild/Role/User\n        :return:\n        \"\"\"\n        await self.set({})\n\n    async def clear_all(self):\n        \"\"\"\n        Removes all data from all entries.\n        :return:\n        \"\"\"\n        await self._super_group.set({})\n\n\nclass MemberGroup(Group):\n    @property\n    def _super_group(self) -> Group:\n        new_identifiers = self.identifiers[:2]\n        group_obj = Group(\n            identifiers=new_identifiers,\n            defaults={},\n            spawner=self.spawner\n        )\n        return group_obj\n\n    @property\n    def _guild_group(self) -> Group:\n        new_identifiers = self.identifiers[:3]\n        group_obj = Group(\n            identifiers=new_identifiers,\n            defaults={},\n            spawner=self.spawner\n        )\n        return group_obj\n\n    def all_guilds(self) -> dict:\n        \"\"\"\n        Gets a dict of all guilds and members.\n\n        REMEMBER: ID's are stored in these dicts as STRINGS.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._super_group()\n\n    def all(self) -> dict:\n        \"\"\"\n        Returns the dict of all members in the same guild.\n        :return:\n        \"\"\"\n        # noinspection PyTypeChecker\n        return self._guild_group()\n\nclass Config:\n    GLOBAL = \"GLOBAL\"\n    GUILD = \"GUILD\"\n    CHANNEL = \"TEXTCHANNEL\"\n    ROLE = \"ROLE\"\n    USER = \"USER\"\n    MEMBER = \"MEMBER\"\n\n    def __init__(self, cog_name: str, unique_identifier: str,\n                 driver_spawn: Callable,\n                 force_registration: bool=False,\n                 defaults: dict=None):\n        self.cog_name = cog_name\n        self.unique_identifier = unique_identifier\n\n        self.spawner = driver_spawn\n        self.force_registration = force_registration\n        self.defaults = defaults or {}\n\n    @classmethod\n    def get_conf(cls, cog_instance, identifier: int,\n                 force_registration=False):\n        \"\"\"\n        Returns a Config instance based on a simplified set of initial\n            variables.\n        :param cog_instance:\n        :param identifier: Any random integer, used to keep your data\n            distinct from any other cog with the same name.\n        :param force_registration: Should config require registration\n            of data keys before allowing you to get/set values?\n        :return:\n        \"\"\"\n        cog_name = cog_instance.__class__.__name__\n        uuid = str(hash(identifier))\n\n        spawner = JSONDriver(cog_name)\n        return cls(cog_name=cog_name, unique_identifier=uuid,\n                   force_registration=force_registration,\n                   driver_spawn=spawner)\n\n    @classmethod\n    def get_core_conf(cls, force_registration: bool=False):\n        core_data_path = Path.cwd() / 'core' / '.data'\n        driver_spawn = JSONDriver(\"Core\", data_path_override=core_data_path)\n        return cls(cog_name=\"Core\", driver_spawn=driver_spawn,\n                   unique_identifier='0',\n                   force_registration=force_registration)\n\n    def __getattr__(self, item: str) -> Union[Group, Value]:\n        \"\"\"\n        This is used to generate Value or Group objects for global\n            values.\n        :param item:\n        :return:\n        \"\"\"\n        global_group = self._get_base_group(self.GLOBAL)\n        return getattr(global_group, item)\n\n    @staticmethod\n    def _get_defaults_dict(key: str, value) -> dict:\n        \"\"\"\n        Since we're allowing nested config stuff now, not storing the\n            defaults as a flat dict sounds like a good idea. May turn\n            out to be an awful one but we'll see.\n        :param key:\n        :param value:\n        :return:\n        \"\"\"\n        ret = {}\n        partial = ret\n        splitted = key.split('__')\n        for i, k in enumerate(splitted, start=1):\n            if not k.isidentifier():\n                raise RuntimeError(\"'{}' is an invalid config key.\".format(k))\n            if i == len(splitted):\n                partial[k] = value\n            else:\n                partial[k] = {}\n                partial = partial[k]\n        return ret\n\n    @staticmethod\n    def _update_defaults(to_add: dict, _partial: dict):\n        \"\"\"\n        This tries to update the defaults dictionary with the nested\n            partial dict generated by _get_defaults_dict. This WILL\n            throw an error if you try to have both a value and a group\n            registered under the same name.\n        :param to_add:\n        :param _partial:\n        :return:\n        \"\"\"\n        for k, v in to_add.items():\n            val_is_dict = isinstance(v, dict)\n            if k in _partial:\n                existing_is_dict = isinstance(_partial[k], dict)\n                if val_is_dict != existing_is_dict:\n                    # != is XOR\n                    raise KeyError(\"You cannot register a Group and a Value under\"\n                                   \" the same name.\")\n                if val_is_dict:\n                    Config._update_defaults(v, _partial=_partial[k])\n                else:\n                    _partial[k] = v\n            else:\n                _partial[k] = v\n\n    def _register_default(self, key: str, **kwargs):\n        if key not in self.defaults:\n            self.defaults[key] = {}\n\n        data = deepcopy(kwargs)\n\n        for k, v in data.items():\n            to_add = self._get_defaults_dict(k, v)\n            self._update_defaults(to_add, self.defaults[key])\n\n    def register_global(self, **kwargs):\n        self._register_default(self.GLOBAL, **kwargs)\n\n    def register_guild(self, **kwargs):\n        self._register_default(self.GUILD, **kwargs)\n\n    def register_channel(self, **kwargs):\n        # We may need to add a voice channel category later\n        self._register_default(self.CHANNEL, **kwargs)\n\n    def register_role(self, **kwargs):\n        self._register_default(self.ROLE, **kwargs)\n\n    def register_user(self, **kwargs):\n        self._register_default(self.USER, **kwargs)\n\n    def register_member(self, **kwargs):\n        self._register_default(self.MEMBER, **kwargs)\n\n    def _get_base_group(self, key: str, *identifiers: str,\n                        group_class=Group) -> Group:\n        # noinspection PyTypeChecker\n        return group_class(\n            identifiers=(self.unique_identifier, key) + identifiers,\n            defaults=self.defaults.get(key, {}),\n            spawner=self.spawner,\n            force_registration=self.force_registration\n        )\n\n    def guild(self, guild: discord.Guild) -> Group:\n        return self._get_base_group(self.GUILD, guild.id)\n\n    def channel(self, channel: discord.TextChannel) -> Group:\n        return self._get_base_group(self.CHANNEL, channel.id)\n\n    def role(self, role: discord.Role) -> Group:\n        return self._get_base_group(self.ROLE, role.id)\n\n    def user(self, user: discord.User) -> Group:\n        return self._get_base_group(self.USER, user.id)\n\n    def member(self, member: discord.Member) -> MemberGroup:\n        return self._get_base_group(self.MEMBER, member.guild.id, member.id,\n                                    group_class=MemberGroup)\n\n", "path": "core/config.py"}]}