From 18badbc60f1ed9e9225d5f38785a5ee3c4ab0932 Mon Sep 17 00:00:00 2001
From: Nadir Chowdhury <chowdhurynadir0@outlook.com>
Date: Sun, 18 Apr 2021 12:56:38 +0100
Subject: [PATCH] Add typing for `utils.cached(_slot)_property`

---
 discord/utils.py | 49 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 33 insertions(+), 16 deletions(-)

diff --git a/discord/utils.py b/discord/utils.py
index 9807c6ace..8e588f7fc 100644
--- a/discord/utils.py
+++ b/discord/utils.py
@@ -21,11 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE.
 """
+from __future__ import annotations
 
 import array
 import asyncio
 import collections.abc
-from typing import Any, Optional, overload
+from typing import Any, Callable, Generic, Optional, Type, TypeVar, overload, TYPE_CHECKING
 import unicodedata
 from base64 import b64encode
 from bisect import bisect_left
@@ -53,27 +54,43 @@ __all__ = (
 )
 DISCORD_EPOCH = 1420070400000
 
-class cached_property:
-    def __init__(self, function):
-        self.function = function
-        self.__doc__ = getattr(function, '__doc__')
+if TYPE_CHECKING:
+    from functools import cached_property
+else:
+    class cached_property:
+        def __init__(self, function):
+            self.function = function
+            self.__doc__ = getattr(function, '__doc__')
 
-    def __get__(self, instance, owner):
-        if instance is None:
-            return self
+        def __get__(self, instance, owner):
+            if instance is None:
+                return self
+
+            value = self.function(instance)
+            setattr(instance, self.function.__name__, value)
 
-        value = self.function(instance)
-        setattr(instance, self.function.__name__, value)
+            return value
 
-        return value
+FS = TypeVar('FS')
+FR = TypeVar('FR', covariant=True)
+CP = TypeVar('CP', bound='cached_property')
+CSP = TypeVar('CSP', bound='CachedSlotProperty')
 
-class CachedSlotProperty:
-    def __init__(self, name, function):
+class CachedSlotProperty(Generic[FS, FR]):
+    def __init__(self, name: str, function: Callable[[FS], FR]) -> None:
         self.name = name
         self.function = function
         self.__doc__ = getattr(function, '__doc__')
 
-    def __get__(self, instance, owner):
+    @overload
+    def __get__(self: CSP, instance: None, owner: Type[FS]) -> CSP:
+        ...
+
+    @overload
+    def __get__(self, instance: FS, owner: Type[FS]) -> FR:
+        ...
+
+    def __get__(self, instance: Optional[FS], owner: Type[FS]) -> Any:
         if instance is None:
             return self
 
@@ -84,8 +101,8 @@ class CachedSlotProperty:
             setattr(instance, self.name, value)
             return value
 
-def cached_slot_property(name):
-    def decorator(func):
+def cached_slot_property(name: str) -> Callable[[Callable[[FS], FR]], CachedSlotProperty[FS, FR]]:
+    def decorator(func: Callable[[FS], FR]) -> CachedSlotProperty[FS, FR]:
         return CachedSlotProperty(name, func)
     return decorator