From 2617f9ad99978f625ab6a4f87103836d6c5bad92 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 4 Jul 2022 19:07:13 +0200 Subject: [PATCH 01/53] fix: unfuck the branch --- interactions/client/__init__.py | 1 + interactions/client/get.py | 118 ++++++++++++++++++++++++++++++++ interactions/client/get.pyi | 83 ++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 interactions/client/get.py create mode 100644 interactions/client/get.pyi diff --git a/interactions/client/__init__.py b/interactions/client/__init__.py index 5d0b8cdba..4e66cfab0 100644 --- a/interactions/client/__init__.py +++ b/interactions/client/__init__.py @@ -8,4 +8,5 @@ from .context import * # noqa: F401 F403 from .decor import * # noqa: F401 F403 from .enums import * # noqa: F401 F403 +from .get import * # noqa: F401 F403 from .models import * # noqa: F401 F403 diff --git a/interactions/client/get.py b/interactions/client/get.py new file mode 100644 index 000000000..482f6909b --- /dev/null +++ b/interactions/client/get.py @@ -0,0 +1,118 @@ +from asyncio import sleep +from inspect import isfunction +from logging import getLogger +from typing import Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args + +from interactions.api.models.channel import Channel +from interactions.api.models.guild import Guild +from interactions.api.models.message import Emoji +from interactions.api.models.role import Role +from interactions.client.bot import Client + +log = getLogger("get") + +_T = TypeVar("_T") + +__all__ = ("get",) + + +def get(*args, **kwargs): + # sourcery no-metrics + + if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): + raise TypeError("You can only use Iterables as single-argument!") + + if len(args) == 2: + client: Client + obj: Union[Type[_T], Type[List[_T]]] + + client, obj = args + if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): + raise TypeError("The object must not be an instance of a class!") + + if isinstance(obj, _GenericAlias): + _obj = get_args(obj)[0] + _objects: List[_obj] = [] + _name = f"get_{_obj.__name__.lower()}" + + # TODO: add cache, include getting for IDs that are `None` + + if len(list(kwargs)) == 2: + if guild_id := kwargs.pop("guild_id", None): + _guild = Guild(**await client._http.get_guild(guild_id), _client=client._http) + _func = getattr(_guild, _name) + + elif channel_id := kwargs.pop("channel_id", None): + _channel = Channel( + **await client._http.get_channel(channel_id), _client=client._http + ) + _func = getattr(_channel, _name) + + else: + _func = getattr(client._http, _name) + + _kwarg_name = list(kwargs)[0][:-1] + + for kwarg in kwargs.get(list(kwargs)[0]): + _kwargs = {_kwarg_name: kwarg} + __obj = await _func(**_kwargs) + + if isinstance(__obj, dict): + _objects.append(_obj(**__obj, _client=client._http)) + else: + _objects.append(__obj) + + return _objects + + if obj in (Role, Emoji): + _guild = Guild( + **await client._http.get_guild(kwargs.pop("guild_id")), _client=client._http + ) + _func = getattr(_guild, _name) + return await _func(**kwargs) + + _func = getattr(client._http, _name) + _obj = await _func(**kwargs) + return obj(**_obj, _client=client._http) + + elif len(args) == 1: + + def run_check(_obj, _check): + return _check(_obj) + + item: Iterable = args[0] + if not isinstance(item, Iterable): + raise TypeError("The specified item must be an iterable!") + + if not kwargs: + raise ValueError( + "You have to specify either the name, id or a custom check to check against!" + ) + + if len(list(kwargs)) > 1: + raise ValueError("Only one keyword argument to check against is allowed!") + + _arg = str(list(kwargs)[0]) + + __obj = next( + ( + _ + for _ in item + if ( + str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) + if not isfunction(kwargs.get(_arg)) + else run_check(item, kwargs.get(_arg)) + ) + ), + None, + ) + return __obj + + +async def __http_request(obj: _T) -> _T: + ... + + +async def _cache(obj: _T) -> _T: + await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once + return obj diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi new file mode 100644 index 000000000..db40fb3d1 --- /dev/null +++ b/interactions/client/get.pyi @@ -0,0 +1,83 @@ +from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, Awaitable + +from interactions.client.bot import Client + +from interactions.api.models.channel import Channel +from interactions.api.models.guild import Guild +from interactions.api.models.member import Member +from interactions.api.models.message import Message, Emoji, Sticker +from interactions.api.models.role import Role +from interactions.api.models.user import User +from interactions.api.models.webhook import Webhook + +_T = TypeVar("_T") + +# not HTTP related +@overload +def get( + item: Iterable, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool] +) -> _T: ... + +# single objects +@overload +def get(client: Client, obj: Type[Channel], *, channel_id: int) -> Awaitable[Channel]: ... +@overload +def get(client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int) -> Awaitable[Emoji]: ... +@overload +def get(client: Client, obj: Type[Guild], *, guild_id: int) -> Awaitable[Guild]: ... +@overload +def get(client: Client, obj: Type[Member], *, guild_id: int, member_id: int) -> Awaitable[Member]: ... +@overload +def get( + client: Client, + obj: Type[Message], + *, + channel_id: int, + message_id: int, +) -> Awaitable[Message]: ... +@overload +def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int) -> Awaitable[Role]: ... +@overload +def get(client: Client, obj: Type[Sticker], *, sticker_id: int) -> Awaitable[Sticker]: ... +@overload +def get(client: Client, obj: Type[User], *, user_id: int) -> Awaitable[User]: ... +@overload +def get(client: Client, obj: Type[Webhook], *, webhook_id: int) -> Awaitable[Webhook]: ... + +# list of objects +@overload +def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int]) -> Awaitable[List[Channel]]: ... +@overload +def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int]) -> Awaitable[List[Emoji]]: ... +@overload +def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int]) -> Awaitable[List[Guild]]: ... +@overload +def get( + client: Client, + obj: Type[List[Member]], + *, + guild_id: int, + member_ids: List[int] +) -> Awaitable[List[Member]]: ... +@overload +def get( + client: Client, + obj: Type[List[Message]], + *, + channel_id: int, + message_ids: List[int], +) -> Awaitable[List[Message]]: ... +@overload +def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int]) -> Awaitable[List[Role]]: ... +@overload +def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int]) -> Awaitable[List[Sticker]]: ... +@overload +def get(client: Client, obj: Type[List[User]], *, user_ids: List[int]) -> Awaitable[List[User]]: ... +@overload +def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int]) -> Awaitable[List[Webhook]]: ... + +# with cache force + + +# Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, +# so we'll leave that out From 87e0966ad200b3dfe34eb953cf5d86a825b7dbad Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 4 Jul 2022 21:37:34 +0200 Subject: [PATCH 02/53] feat: Add single-arg cache --- interactions/client/get.py | 92 ++++++++++++++++++++++++++----------- interactions/client/get.pyi | 12 +++-- 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 482f6909b..124cbbcc7 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,13 +1,17 @@ from asyncio import sleep from inspect import isfunction from logging import getLogger -from typing import Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args - -from interactions.api.models.channel import Channel -from interactions.api.models.guild import Guild -from interactions.api.models.message import Emoji -from interactions.api.models.role import Role -from interactions.client.bot import Client +from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args + +from ..api.error import LibraryException +from ..api.http.client import HTTPClient +from ..api.models.channel import Channel +from ..api.models.guild import Guild +from ..api.models.member import Member +from ..api.models.message import Emoji +from ..api.models.misc import Snowflake +from ..api.models.role import Role +from .bot import Client log = getLogger("get") @@ -20,7 +24,12 @@ def get(*args, **kwargs): # sourcery no-metrics if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): - raise TypeError("You can only use Iterables as single-argument!") + raise LibraryException(message="You can only use Iterables as single-argument!", code=12) + + if kwargs.get("force_http", None) and kwargs.get("force_cache", None): + raise LibraryException( + message="`force_cache` and `force_http` are mutually exclusive", code=12 + ) if len(args) == 2: client: Client @@ -28,12 +37,14 @@ def get(*args, **kwargs): client, obj = args if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): - raise TypeError("The object must not be an instance of a class!") + raise LibraryException( + message="The object must not be an instance of a class!", code=12 + ) + _name = f"get_{obj.__name__.lower()}" if isinstance(obj, _GenericAlias): _obj = get_args(obj)[0] _objects: List[_obj] = [] - _name = f"get_{_obj.__name__.lower()}" # TODO: add cache, include getting for IDs that are `None` @@ -64,16 +75,38 @@ def get(*args, **kwargs): return _objects - if obj in (Role, Emoji): - _guild = Guild( - **await client._http.get_guild(kwargs.pop("guild_id")), _client=client._http - ) - _func = getattr(_guild, _name) - return await _func(**kwargs) + _obj: _T = None + + if not (force_http := kwargs.get("force_http", False)): + if isinstance(obj, Member): + _values = ( + kwargs.get("guild_id"), + kwargs.get("member_id"), + ) # Fuck it, I can't be dynamic on this + else: + if len(kwargs) == 2: + kwargs.pop("channel_id", None) + kwargs.pop("guild__id", None) + _values = Snowflake(kwargs.get(list(kwargs)[0])) - _func = getattr(client._http, _name) - _obj = await _func(**kwargs) - return obj(**_obj, _client=client._http) + _obj = client.cache[obj].get(_values) + + if kwargs.get("force_cache", False): + return _obj + + elif not force_http and obj: + return __cache(obj) + + else: + if obj in (Role, Emoji): + _guild = Guild( + **await client._http.get_guild(kwargs.pop("guild_id")), _client=client._http + ) + _func = getattr(_guild, _name) + return await _func(**kwargs) + + _func = getattr(client._http, _name) + return __http_request(obj, _func(**kwargs), client._http) elif len(args) == 1: @@ -82,15 +115,18 @@ def run_check(_obj, _check): item: Iterable = args[0] if not isinstance(item, Iterable): - raise TypeError("The specified item must be an iterable!") + raise LibraryException(message="The specified item must be an iterable!", code=12) if not kwargs: - raise ValueError( - "You have to specify either the name, id or a custom check to check against!" + raise LibraryException( + message="You have to specify either the name, id or a custom check to check against!", + code=12, ) if len(list(kwargs)) > 1: - raise ValueError("Only one keyword argument to check against is allowed!") + raise LibraryException( + message="Only one keyword argument to check against is allowed!", code=12 + ) _arg = str(list(kwargs)[0]) @@ -109,10 +145,14 @@ def run_check(_obj, _check): return __obj -async def __http_request(obj: _T) -> _T: - ... +async def __http_request( + obj: Union[Type[_T], List[Type[_T]]], request: Coroutine, http: HTTPClient +) -> Union[_T, List[_T]]: + if not isinstance(obj, list): + _obj = await request + return obj(**_obj, _client=http) -async def _cache(obj: _T) -> _T: +async def __cache(obj: _T) -> _T: await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once return obj diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index db40fb3d1..0bf38bebf 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -20,13 +20,17 @@ def get( # single objects @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int) -> Awaitable[Channel]: ... +def get(client: Client, obj: Type[Channel], *, channel_id: int, force_http: bool = False) -> Awaitable[Channel]: ... @overload -def get(client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int) -> Awaitable[Emoji]: ... +def get( + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_http: bool = False +) -> Awaitable[Emoji]: ... @overload -def get(client: Client, obj: Type[Guild], *, guild_id: int) -> Awaitable[Guild]: ... +def get(client: Client, obj: Type[Guild], *, guild_id: int, force_http: bool = False) -> Awaitable[Guild]: ... @overload -def get(client: Client, obj: Type[Member], *, guild_id: int, member_id: int) -> Awaitable[Member]: ... +def get( + client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_http: bool = False +) -> Awaitable[Member]: ... @overload def get( client: Client, From bed9aca3ce252a9cfa2d0b2b9a903f479a860f01 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:06:39 +0200 Subject: [PATCH 03/53] feat: more work on get --- interactions/client/get.py | 90 ++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 124cbbcc7..41ccc9bc3 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -5,7 +5,6 @@ from ..api.error import LibraryException from ..api.http.client import HTTPClient -from ..api.models.channel import Channel from ..api.models.guild import Guild from ..api.models.member import Member from ..api.models.message import Emoji @@ -42,12 +41,50 @@ def get(*args, **kwargs): ) _name = f"get_{obj.__name__.lower()}" + __name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias): _obj = get_args(obj)[0] _objects: List[_obj] = [] # TODO: add cache, include getting for IDs that are `None` + force_cache = kwargs.pop("force_cache", False) + + if not (force_http := kwargs.pop("force_http", False)): + __name += "s" + if isinstance(_obj, Member): # Can't be more dynamic on this + _values = () + _guild_id = Snowflake(kwargs.get("guild_id")) + for _id in kwargs.get("member_ids"): + _values += ( + ( + _guild_id, + Snowflake(_id), + ), + ) + for item in _values: + _objects.append(client.cache[obj].get(item)) + + else: + kwargs.get("channel_id", None) + kwargs.get("guild_id", None) + for _id in kwargs.get(__name): + _objects.append(client.cache[obj].get(_id)) + + if force_cache: + return _objects + + elif not force_http and None not in _objects: + return __cache(_objects) + + else: + _func = getattr(_name, client._http) + for _index, __obj in enumerate(_objects): + if __obj is None: + _id = kwargs.get(__name)[_index] + _request = _func() # noqa + + """ if len(list(kwargs)) == 2: if guild_id := kwargs.pop("guild_id", None): _guild = Guild(**await client._http.get_guild(guild_id), _client=client._http) @@ -74,39 +111,33 @@ def get(*args, **kwargs): _objects.append(__obj) return _objects - + """ _obj: _T = None - if not (force_http := kwargs.get("force_http", False)): + force_cache = kwargs.pop("force_cache", False) + + if not (force_http := kwargs.pop("force_http", False)): if isinstance(obj, Member): _values = ( - kwargs.get("guild_id"), - kwargs.get("member_id"), + Snowflake(kwargs.get("guild_id")), + Snowflake(kwargs.get("member_id")), ) # Fuck it, I can't be dynamic on this else: if len(kwargs) == 2: - kwargs.pop("channel_id", None) - kwargs.pop("guild__id", None) - _values = Snowflake(kwargs.get(list(kwargs)[0])) + kwargs.get("channel_id", None) + kwargs.get("guild_id", None) + _values = Snowflake(kwargs.get(__name)) _obj = client.cache[obj].get(_values) - if kwargs.get("force_cache", False): + if force_cache: return _obj - elif not force_http and obj: - return __cache(obj) + elif not force_http and _obj: + return __cache(_obj) else: - if obj in (Role, Emoji): - _guild = Guild( - **await client._http.get_guild(kwargs.pop("guild_id")), _client=client._http - ) - _func = getattr(_guild, _name) - return await _func(**kwargs) - - _func = getattr(client._http, _name) - return __http_request(obj, _func(**kwargs), client._http) + return __http_request(obj=obj, requests=None, http=client._http, _name=_name, **kwargs) elif len(args) == 1: @@ -146,12 +177,25 @@ def run_check(_obj, _check): async def __http_request( - obj: Union[Type[_T], List[Type[_T]]], request: Coroutine, http: HTTPClient + obj: Union[Type[_T], List[Type[_T]]], + http: HTTPClient, + request: Coroutine = None, + _name=None, + **kwargs, ) -> Union[_T, List[_T]]: - if not isinstance(obj, list): - _obj = await request + + if not request: + if obj in (Role, Emoji): + _guild = Guild(**await http.get_guild(kwargs.pop("guild_id")), _client=http) + _func = getattr(_guild, _name) + return await _func(**kwargs) + + _func = getattr(http, _name) + _obj = await _func return obj(**_obj, _client=http) + return obj(**await request, _client=http) + async def __cache(obj: _T) -> _T: await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once From 3f19ef61bf29576a7638c56e9b01df3cc2317323 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:19:54 +0200 Subject: [PATCH 04/53] Update get.pyi --- interactions/client/get.pyi | 103 ++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 0bf38bebf..f378aa82f 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -12,24 +12,27 @@ from interactions.api.models.webhook import Webhook _T = TypeVar("_T") -# not HTTP related +# not API-object related @overload def get( item: Iterable, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool] ) -> _T: ... +# API-object related + +#with http force # single objects @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int, force_http: bool = False) -> Awaitable[Channel]: ... +def get(client: Client, obj: Type[Channel], *, channel_id: int, force_http: Optional[bool]) -> Awaitable[Channel]: ... @overload def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_http: bool = False + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_http: Optional[bool] ) -> Awaitable[Emoji]: ... @overload -def get(client: Client, obj: Type[Guild], *, guild_id: int, force_http: bool = False) -> Awaitable[Guild]: ... +def get(client: Client, obj: Type[Guild], *, guild_id: int, force_http: Optional[bool]) -> Awaitable[Guild]: ... @overload def get( - client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_http: bool = False + client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_http: Optional[bool] ) -> Awaitable[Member]: ... @overload def get( @@ -38,30 +41,32 @@ def get( *, channel_id: int, message_id: int, + force_http: Optional[bool], ) -> Awaitable[Message]: ... @overload -def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int) -> Awaitable[Role]: ... +def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_http: Optional[bool]) -> Awaitable[Role]: ... @overload -def get(client: Client, obj: Type[Sticker], *, sticker_id: int) -> Awaitable[Sticker]: ... +def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force_http: Optional[bool]) -> Awaitable[Sticker]: ... @overload -def get(client: Client, obj: Type[User], *, user_id: int) -> Awaitable[User]: ... +def get(client: Client, obj: Type[User], *, user_id: int, force_http: Optional[bool]) -> Awaitable[User]: ... @overload -def get(client: Client, obj: Type[Webhook], *, webhook_id: int) -> Awaitable[Webhook]: ... +def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force_http: Optional[bool]) -> Awaitable[Webhook]: ... # list of objects @overload -def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int]) -> Awaitable[List[Channel]]: ... +def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Channel]]: ... @overload -def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int]) -> Awaitable[List[Emoji]]: ... +def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Emoji]]: ... @overload -def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int]) -> Awaitable[List[Guild]]: ... +def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Guild]]: ... @overload def get( client: Client, obj: Type[List[Member]], *, guild_id: int, - member_ids: List[int] + member_ids: List[int], + force_http: Optional[bool] ) -> Awaitable[List[Member]]: ... @overload def get( @@ -70,17 +75,81 @@ def get( *, channel_id: int, message_ids: List[int], + force_http: Optional[bool] ) -> Awaitable[List[Message]]: ... @overload -def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int]) -> Awaitable[List[Role]]: ... +def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Role]]: ... @overload -def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int]) -> Awaitable[List[Sticker]]: ... +def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Sticker]]: ... @overload -def get(client: Client, obj: Type[List[User]], *, user_ids: List[int]) -> Awaitable[List[User]]: ... +def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[User]]: ... @overload -def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int]) -> Awaitable[List[Webhook]]: ... +def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Webhook]]: ... # with cache force +@overload +def get(client: Client, obj: Type[Channel], *, channel_id: int, force_cache: Optional[bool] = True) -> Channel: ... +@overload +def get( + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_cache: Optional[bool] = True +) -> Awaitable[Emoji]: ... +@overload +def get(client: Client, obj: Type[Guild], *, guild_id: int, force_cache: Optional[bool] = True) -> Guild: ... +@overload +def get( + client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_cache: Optional[bool] = True +) -> Member: ... +@overload +def get( + client: Client, + obj: Type[Message], + *, + channel_id: int, + message_id: int, + force_cache: Optional[bool] = True, +) -> Message: ... +@overload +def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_cache: Optional[bool] = True) -> Role: ... +@overload +def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force_cache: Optional[bool] = True) -> Sticker: ... +@overload +def get(client: Client, obj: Type[User], *, user_id: int, force_cache: Optional[bool] = True) -> User: ... +@overload +def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force_cache: Optional[bool] = True) -> Webhook: ... + +# list of objects +@overload +def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force_cache: Optional[bool] = True) -> List[Channel]: ... +@overload +def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force_cache: Optional[bool] = True) -> List[Emoji]: ... +@overload +def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force_cache: Optional[bool] = True) -> List[Guild]: ... +@overload +def get( + client: Client, + obj: Type[List[Member]], + *, + guild_id: int, + member_ids: List[int], + force_cache: Optional[bool] = True +) -> List[Member]: ... +@overload +def get( + client: Client, + obj: Type[List[Message]], + *, + channel_id: int, + message_ids: List[int], + force_cache: Optional[bool] = True +) -> List[Message]: ... +@overload +def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force_cache: Optional[bool] = True) -> List[Role]: ... +@overload +def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force_cache: Optional[bool] = True) -> List[Sticker]: ... +@overload +def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force_cache: Optional[bool] = True) -> List[User]: ... +@overload +def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force_cache: Optional[bool] = True) -> List[Webhook]: ... # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, From f9f635bbacda9d4e869ba6d846cd3b96053f5c90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 21:20:11 +0000 Subject: [PATCH 05/53] ci: correct from checks. --- interactions/client/get.pyi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index f378aa82f..08b09ceb7 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -20,7 +20,7 @@ def get( # API-object related -#with http force +#with http force # single objects @overload def get(client: Client, obj: Type[Channel], *, channel_id: int, force_http: Optional[bool]) -> Awaitable[Channel]: ... @@ -41,7 +41,7 @@ def get( *, channel_id: int, message_id: int, - force_http: Optional[bool], + force_http: Optional[bool], ) -> Awaitable[Message]: ... @overload def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_http: Optional[bool]) -> Awaitable[Role]: ... @@ -65,7 +65,7 @@ def get( obj: Type[List[Member]], *, guild_id: int, - member_ids: List[int], + member_ids: List[int], force_http: Optional[bool] ) -> Awaitable[List[Member]]: ... @overload @@ -106,7 +106,7 @@ def get( *, channel_id: int, message_id: int, - force_cache: Optional[bool] = True, + force_cache: Optional[bool] = True, ) -> Message: ... @overload def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_cache: Optional[bool] = True) -> Role: ... @@ -130,7 +130,7 @@ def get( obj: Type[List[Member]], *, guild_id: int, - member_ids: List[int], + member_ids: List[int], force_cache: Optional[bool] = True ) -> List[Member]: ... @overload From 5b54e585bc2c28cea1dd9f0f9fea97ba6421ffde Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:54:44 +0200 Subject: [PATCH 06/53] Update get.py --- interactions/client/get.py | 63 ++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 41ccc9bc3..6b9d5ebbd 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,5 +1,5 @@ from asyncio import sleep -from inspect import isfunction +from inspect import isfunction, isawaitable from logging import getLogger from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args @@ -27,7 +27,7 @@ def get(*args, **kwargs): if kwargs.get("force_http", None) and kwargs.get("force_cache", None): raise LibraryException( - message="`force_cache` and `force_http` are mutually exclusive", code=12 + message="`force_cache` and `force_http` are mutually exclusive!", code=12 ) if len(args) == 2: @@ -46,8 +46,6 @@ def get(*args, **kwargs): _obj = get_args(obj)[0] _objects: List[_obj] = [] - # TODO: add cache, include getting for IDs that are `None` - force_cache = kwargs.pop("force_cache", False) if not (force_http := kwargs.pop("force_http", False)): @@ -69,49 +67,38 @@ def get(*args, **kwargs): kwargs.get("channel_id", None) kwargs.get("guild_id", None) for _id in kwargs.get(__name): - _objects.append(client.cache[obj].get(_id)) + _objects.append(client.cache[obj].get(Snowflake(_id), None)) if force_cache: return _objects elif not force_http and None not in _objects: return __cache(_objects) + + elif force_http: + _objects.clear() + _func = getattr(_name, client._http) + for _id in kwargs.get(__name): + _kwargs = kwargs + _kwargs.pop(__name) + _kwargs[__name[:-1]] = _id + _objects.append(_func(**_kwargs) + + return __http_request(_obj, request=_objects, http=client.http) else: _func = getattr(_name, client._http) for _index, __obj in enumerate(_objects): if __obj is None: _id = kwargs.get(__name)[_index] - _request = _func() # noqa - - """ - if len(list(kwargs)) == 2: - if guild_id := kwargs.pop("guild_id", None): - _guild = Guild(**await client._http.get_guild(guild_id), _client=client._http) - _func = getattr(_guild, _name) - - elif channel_id := kwargs.pop("channel_id", None): - _channel = Channel( - **await client._http.get_channel(channel_id), _client=client._http - ) - _func = getattr(_channel, _name) + _kwargs = kwargs + _kwargs.pop(__name) + _kwargs[__name[:-1]] = _id + _request = _func(**_kwargs) + _objects[_index] = _request - else: - _func = getattr(client._http, _name) - - _kwarg_name = list(kwargs)[0][:-1] - - for kwarg in kwargs.get(list(kwargs)[0]): - _kwargs = {_kwarg_name: kwarg} - __obj = await _func(**_kwargs) - - if isinstance(__obj, dict): - _objects.append(_obj(**__obj, _client=client._http)) - else: - _objects.append(__obj) + return __http_request(_obj, request=_objects, http=client._http) - return _objects - """ _obj: _T = None force_cache = kwargs.pop("force_cache", False) @@ -137,7 +124,7 @@ def get(*args, **kwargs): return __cache(_obj) else: - return __http_request(obj=obj, requests=None, http=client._http, _name=_name, **kwargs) + return __http_request(obj=obj, request=None, http=client._http, _name=_name, **kwargs) elif len(args) == 1: @@ -177,9 +164,9 @@ def run_check(_obj, _check): async def __http_request( - obj: Union[Type[_T], List[Type[_T]]], + obj: Type[_T]], http: HTTPClient, - request: Coroutine = None, + request: Union[Coroutine, List[_T, Coroutine]] = None, _name=None, **kwargs, ) -> Union[_T, List[_T]]: @@ -194,8 +181,10 @@ async def __http_request( _obj = await _func return obj(**_obj, _client=http) - return obj(**await request, _client=http) + if not isinstance(request, list): + return obj(**await request, _client=http) + return [obj(**await req, _client=http) if isawaitable(req) else req for req in request] async def __cache(obj: _T) -> _T: await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once From 7e70bdfc7f28a970af42029c0cbe02b3e24e17c5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 21:55:05 +0000 Subject: [PATCH 07/53] ci: correct from checks. --- interactions/client/get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 6b9d5ebbd..366ccc9ef 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,5 +1,5 @@ from asyncio import sleep -from inspect import isfunction, isawaitable +from inspect import isawaitable, isfunction from logging import getLogger from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args @@ -74,7 +74,7 @@ def get(*args, **kwargs): elif not force_http and None not in _objects: return __cache(_objects) - + elif force_http: _objects.clear() _func = getattr(_name, client._http) From 44d5a424d6cd30dc2fb3bde5599bafb8fec867ca Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:07:53 +0200 Subject: [PATCH 08/53] Update get.py --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 366ccc9ef..c231c9301 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -84,7 +84,7 @@ def get(*args, **kwargs): _kwargs[__name[:-1]] = _id _objects.append(_func(**_kwargs) - return __http_request(_obj, request=_objects, http=client.http) + return __http_request(_obj, request=_objects, http=client._http) else: _func = getattr(_name, client._http) From 207fb4c79977b08616d373711abc77e25ce9dbc9 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:22:20 +0200 Subject: [PATCH 09/53] Update get.py --- interactions/client/get.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index c231c9301..0756f7237 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -45,11 +45,11 @@ def get(*args, **kwargs): if isinstance(obj, _GenericAlias): _obj = get_args(obj)[0] _objects: List[_obj] = [] + __name += "s" force_cache = kwargs.pop("force_cache", False) - if not (force_http := kwargs.pop("force_http", False)): - __name += "s" + if not (force_http := kwargs.pop("force_http", False): if isinstance(_obj, Member): # Can't be more dynamic on this _values = () _guild_id = Snowflake(kwargs.get("guild_id")) @@ -82,9 +82,8 @@ def get(*args, **kwargs): _kwargs = kwargs _kwargs.pop(__name) _kwargs[__name[:-1]] = _id - _objects.append(_func(**_kwargs) - - return __http_request(_obj, request=_objects, http=client._http) + _objects.append(_func(**_kwargs)) + return __http_request(_obj, request=_objects, http=client._http) else: _func = getattr(_name, client._http) @@ -96,7 +95,6 @@ def get(*args, **kwargs): _kwargs[__name[:-1]] = _id _request = _func(**_kwargs) _objects[_index] = _request - return __http_request(_obj, request=_objects, http=client._http) _obj: _T = None From 58dbe13a73bea934250fee5a668e5901c402bfff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:23:42 +0000 Subject: [PATCH 10/53] ci: correct from checks. --- interactions/client/get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 0756f7237..db1b631c8 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -45,7 +45,7 @@ def get(*args, **kwargs): if isinstance(obj, _GenericAlias): _obj = get_args(obj)[0] _objects: List[_obj] = [] - __name += "s" + __name += "s" force_cache = kwargs.pop("force_cache", False) @@ -83,7 +83,7 @@ def get(*args, **kwargs): _kwargs.pop(__name) _kwargs[__name[:-1]] = _id _objects.append(_func(**_kwargs)) - return __http_request(_obj, request=_objects, http=client._http) + return __http_request(_obj, request=_objects, http=client._http) else: _func = getattr(_name, client._http) From e5bb9642d17fd42517a7a5958dd9e462e0be25f4 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:35:35 +0200 Subject: [PATCH 11/53] Update get.py --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index db1b631c8..b24467bfc 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -49,7 +49,7 @@ def get(*args, **kwargs): force_cache = kwargs.pop("force_cache", False) - if not (force_http := kwargs.pop("force_http", False): + if not (force_http := kwargs.pop("force_http", False)): if isinstance(_obj, Member): # Can't be more dynamic on this _values = () _guild_id = Snowflake(kwargs.get("guild_id")) From 6952c9fd8d70ce93eabdfa8450995f84d0295aaf Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:48:19 +0200 Subject: [PATCH 12/53] Update get.py --- interactions/client/get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index b24467bfc..80c3d68f5 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -162,10 +162,10 @@ def run_check(_obj, _check): async def __http_request( - obj: Type[_T]], + obj: Type[_T], http: HTTPClient, request: Union[Coroutine, List[_T, Coroutine]] = None, - _name=None, + _name: str =None, **kwargs, ) -> Union[_T, List[_T]]: From aa1ffdc8360c983a21d532fd1860283ba69c32d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:48:33 +0000 Subject: [PATCH 13/53] ci: correct from checks. --- interactions/client/get.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 80c3d68f5..3fbedafd5 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -165,7 +165,7 @@ async def __http_request( obj: Type[_T], http: HTTPClient, request: Union[Coroutine, List[_T, Coroutine]] = None, - _name: str =None, + _name: str = None, **kwargs, ) -> Union[_T, List[_T]]: @@ -184,6 +184,7 @@ async def __http_request( return [obj(**await req, _client=http) if isawaitable(req) else req for req in request] + async def __cache(obj: _T) -> _T: await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once return obj From d6aacc529e6c01d7cd0dd5680d015faed76b0b88 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:54:39 +0200 Subject: [PATCH 14/53] Update get.py --- interactions/client/get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 3fbedafd5..99b4b4ff2 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,4 +1,4 @@ -from asyncio import sleep +Ich from asyncio import sleep from inspect import isawaitable, isfunction from logging import getLogger from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args @@ -164,7 +164,7 @@ def run_check(_obj, _check): async def __http_request( obj: Type[_T], http: HTTPClient, - request: Union[Coroutine, List[_T, Coroutine]] = None, + request: Union[Coroutine, List[Union[_T, Coroutine]]] = None, _name: str = None, **kwargs, ) -> Union[_T, List[_T]]: From 138c871b1deeb92ef1bb6c871ceb0586b73b33bb Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 07:01:57 +0200 Subject: [PATCH 15/53] Update get.py --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 99b4b4ff2..7796eab23 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,4 +1,4 @@ -Ich from asyncio import sleep +from asyncio import sleep from inspect import isawaitable, isfunction from logging import getLogger from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args From 305fa58070614ee95db9cfae651049565cb5f7e8 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 18:40:07 +0200 Subject: [PATCH 16/53] feat: more work on get --- docs/get.rst | 8 ++++ interactions/client/get.py | 89 +++++++++++++++++++++---------------- interactions/client/get.pyi | 86 ++++++++++++++++++----------------- 3 files changed, 104 insertions(+), 79 deletions(-) create mode 100644 docs/get.rst diff --git a/docs/get.rst b/docs/get.rst new file mode 100644 index 000000000..245fc022d --- /dev/null +++ b/docs/get.rst @@ -0,0 +1,8 @@ +.. currentmodule:: interactions + +Bot Client +========== + +.. automodule:: interactions.client.get + :members: + :noindex: diff --git a/interactions/client/get.py b/interactions/client/get.py index 7796eab23..72e489b21 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -1,4 +1,5 @@ from asyncio import sleep +from enum import Enum from inspect import isawaitable, isfunction from logging import getLogger from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args @@ -16,20 +17,32 @@ _T = TypeVar("_T") -__all__ = ("get",) +__all__ = ( + "get", + "Force", +) + + +class Force(str, Enum): + """ + An enum representing the force methods for the get method + """ + + CACHE = "cache" + HTTP = "http" def get(*args, **kwargs): - # sourcery no-metrics + """ + Helper method to get an object. + + + + """ if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): raise LibraryException(message="You can only use Iterables as single-argument!", code=12) - if kwargs.get("force_http", None) and kwargs.get("force_cache", None): - raise LibraryException( - message="`force_cache` and `force_http` are mutually exclusive!", code=12 - ) - if len(args) == 2: client: Client obj: Union[Type[_T], Type[List[_T]]] @@ -40,16 +53,17 @@ def get(*args, **kwargs): message="The object must not be an instance of a class!", code=12 ) - _name = f"get_{obj.__name__.lower()}" - __name = f"{obj.__name__.lower()}_id" + http_name = f"get_{obj.__name__.lower()}" + kwarg_name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias): _obj = get_args(obj)[0] _objects: List[_obj] = [] - __name += "s" + kwarg_name += "s" - force_cache = kwargs.pop("force_cache", False) + force_cache = kwargs.pop("force", None) == "cache" + force_http = kwargs.pop("force", None) == "http" - if not (force_http := kwargs.pop("force_http", False)): + if not force_http: if isinstance(_obj, Member): # Can't be more dynamic on this _values = () _guild_id = Snowflake(kwargs.get("guild_id")) @@ -64,54 +78,49 @@ def get(*args, **kwargs): _objects.append(client.cache[obj].get(item)) else: - kwargs.get("channel_id", None) - kwargs.get("guild_id", None) - for _id in kwargs.get(__name): + for _id in kwargs.get(kwarg_name): _objects.append(client.cache[obj].get(Snowflake(_id), None)) if force_cache: return _objects elif not force_http and None not in _objects: - return __cache(_objects) + return _cache(_objects) elif force_http: _objects.clear() - _func = getattr(_name, client._http) - for _id in kwargs.get(__name): + _func = getattr(http_name, client._http) + for _id in kwargs.get(kwarg_name): _kwargs = kwargs - _kwargs.pop(__name) - _kwargs[__name[:-1]] = _id + _kwargs.pop(kwarg_name) + _kwargs[kwarg_name[:-1]] = _id _objects.append(_func(**_kwargs)) - return __http_request(_obj, request=_objects, http=client._http) + return _http_request(_obj, http=client._http, request=_objects) else: - _func = getattr(_name, client._http) + _func = getattr(http_name, client._http) for _index, __obj in enumerate(_objects): if __obj is None: - _id = kwargs.get(__name)[_index] + _id = kwargs.get(kwarg_name)[_index] _kwargs = kwargs - _kwargs.pop(__name) - _kwargs[__name[:-1]] = _id + _kwargs.pop(kwarg_name) + _kwargs[kwarg_name[:-1]] = _id _request = _func(**_kwargs) _objects[_index] = _request - return __http_request(_obj, request=_objects, http=client._http) + return _http_request(_obj, http=client._http, request=_objects) _obj: _T = None - force_cache = kwargs.pop("force_cache", False) - - if not (force_http := kwargs.pop("force_http", False)): + force_cache = kwargs.pop("force", None) == "cache" + force_http = kwargs.pop("force", None) == "http" + if not force_http: if isinstance(obj, Member): _values = ( Snowflake(kwargs.get("guild_id")), Snowflake(kwargs.get("member_id")), ) # Fuck it, I can't be dynamic on this else: - if len(kwargs) == 2: - kwargs.get("channel_id", None) - kwargs.get("guild_id", None) - _values = Snowflake(kwargs.get(__name)) + _values = Snowflake(kwargs.get(kwarg_name)) _obj = client.cache[obj].get(_values) @@ -119,10 +128,12 @@ def get(*args, **kwargs): return _obj elif not force_http and _obj: - return __cache(_obj) + return _cache(_obj) else: - return __http_request(obj=obj, request=None, http=client._http, _name=_name, **kwargs) + return _http_request( + obj=obj, http=client._http, request=None, _name=http_name, **kwargs + ) elif len(args) == 1: @@ -161,10 +172,10 @@ def run_check(_obj, _check): return __obj -async def __http_request( +async def _http_request( obj: Type[_T], http: HTTPClient, - request: Union[Coroutine, List[Union[_T, Coroutine]]] = None, + request: Union[Coroutine, List[Union[_T, Coroutine]], List[Coroutine]] = None, _name: str = None, **kwargs, ) -> Union[_T, List[_T]]: @@ -185,6 +196,6 @@ async def __http_request( return [obj(**await req, _client=http) if isawaitable(req) else req for req in request] -async def __cache(obj: _T) -> _T: - await sleep(0.00001) # iirc Bluenix meant that any coroutine should await at least once +async def _cache(obj: _T) -> _T: + await sleep(0) # iirc Bluenix meant that any coroutine should await at least once return obj diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 08b09ceb7..ca03d7bdb 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -1,7 +1,7 @@ -from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, Awaitable +from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, Awaitable, Literal from interactions.client.bot import Client - +from enum import Enum from interactions.api.models.channel import Channel from interactions.api.models.guild import Guild from interactions.api.models.member import Member @@ -12,6 +12,13 @@ from interactions.api.models.webhook import Webhook _T = TypeVar("_T") +class Force(str, Enum): + """ + An enum representing the force methods for the get method + """ + CACHE: str + HTTP: str + # not API-object related @overload def get( @@ -23,16 +30,16 @@ def get( #with http force # single objects @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int, force_http: Optional[bool]) -> Awaitable[Channel]: ... +def get(client: Client, obj: Type[Channel], *, channel_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Channel]: ... @overload def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_http: Optional[bool] + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[Emoji]: ... @overload -def get(client: Client, obj: Type[Guild], *, guild_id: int, force_http: Optional[bool]) -> Awaitable[Guild]: ... +def get(client: Client, obj: Type[Guild], *, guild_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Guild]: ... @overload def get( - client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_http: Optional[bool] + client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[Member]: ... @overload def get( @@ -41,24 +48,24 @@ def get( *, channel_id: int, message_id: int, - force_http: Optional[bool], + force: Optional[Literal["http"]] = None, ) -> Awaitable[Message]: ... @overload -def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_http: Optional[bool]) -> Awaitable[Role]: ... +def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Role]: ... @overload -def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force_http: Optional[bool]) -> Awaitable[Sticker]: ... +def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Sticker]: ... @overload -def get(client: Client, obj: Type[User], *, user_id: int, force_http: Optional[bool]) -> Awaitable[User]: ... +def get(client: Client, obj: Type[User], *, user_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[User]: ... @overload -def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force_http: Optional[bool]) -> Awaitable[Webhook]: ... +def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Webhook]: ... # list of objects @overload -def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Channel]]: ... +def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Channel]]: ... @overload -def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Emoji]]: ... +def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Emoji]]: ... @overload -def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Guild]]: ... +def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Guild]]: ... @overload def get( client: Client, @@ -66,7 +73,7 @@ def get( *, guild_id: int, member_ids: List[int], - force_http: Optional[bool] + force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[List[Member]]: ... @overload def get( @@ -75,29 +82,29 @@ def get( *, channel_id: int, message_ids: List[int], - force_http: Optional[bool] + force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[List[Message]]: ... @overload -def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Role]]: ... +def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Role]]: ... @overload -def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Sticker]]: ... +def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Sticker]]: ... @overload -def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[User]]: ... +def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[User]]: ... @overload -def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force_http: Optional[bool]) -> Awaitable[List[Webhook]]: ... +def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Webhook]]: ... # with cache force @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int, force_cache: Optional[bool] = True) -> Channel: ... +def get(client: Client, obj: Type[Channel], *, channel_id: int, force: Literal["cache", Force.CACHE]) -> Channel: ... @overload def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force_cache: Optional[bool] = True -) -> Awaitable[Emoji]: ... + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force: Literal["cache", Force.CACHE] +) -> Emoji: ... @overload -def get(client: Client, obj: Type[Guild], *, guild_id: int, force_cache: Optional[bool] = True) -> Guild: ... +def get(client: Client, obj: Type[Guild], *, guild_id: int, force: Literal["cache", Force.CACHE]) -> Guild: ... @overload def get( - client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force_cache: Optional[bool] = True + client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force: Literal["cache", Force.CACHE] ) -> Member: ... @overload def get( @@ -106,24 +113,24 @@ def get( *, channel_id: int, message_id: int, - force_cache: Optional[bool] = True, + force: Literal["cache", Force.CACHE], ) -> Message: ... @overload -def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force_cache: Optional[bool] = True) -> Role: ... +def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force: Literal["cache", Force.CACHE]) -> Role: ... @overload -def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force_cache: Optional[bool] = True) -> Sticker: ... +def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force: Literal["cache", Force.CACHE]) -> Sticker: ... @overload -def get(client: Client, obj: Type[User], *, user_id: int, force_cache: Optional[bool] = True) -> User: ... +def get(client: Client, obj: Type[User], *, user_id: int, force: Literal["cache", Force.CACHE]) -> User: ... @overload -def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force_cache: Optional[bool] = True) -> Webhook: ... +def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force: Literal["cache", Force.CACHE]) -> Webhook: ... # list of objects @overload -def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force_cache: Optional[bool] = True) -> List[Channel]: ... +def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Channel]: ... @overload -def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force_cache: Optional[bool] = True) -> List[Emoji]: ... +def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Emoji]: ... @overload -def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force_cache: Optional[bool] = True) -> List[Guild]: ... +def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Guild]: ... @overload def get( client: Client, @@ -131,7 +138,7 @@ def get( *, guild_id: int, member_ids: List[int], - force_cache: Optional[bool] = True + force: Literal["cache", Force.CACHE] ) -> List[Member]: ... @overload def get( @@ -140,17 +147,16 @@ def get( *, channel_id: int, message_ids: List[int], - force_cache: Optional[bool] = True + force: Literal["cache", Force.CACHE] ) -> List[Message]: ... @overload -def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force_cache: Optional[bool] = True) -> List[Role]: ... +def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Role]: ... @overload -def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force_cache: Optional[bool] = True) -> List[Sticker]: ... +def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Sticker]: ... @overload -def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force_cache: Optional[bool] = True) -> List[User]: ... +def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[User]: ... @overload -def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force_cache: Optional[bool] = True) -> List[Webhook]: ... - +def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Webhook]: ... # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, # so we'll leave that out From 3fcdfbc7e7be971e7a1e782602511239cb3beafe Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:05:27 +0200 Subject: [PATCH 17/53] feat: more work on get --- interactions/client/get.py | 11 ++++++++++- interactions/client/get.pyi | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 72e489b21..9682bcf12 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -2,7 +2,13 @@ from enum import Enum from inspect import isawaitable, isfunction from logging import getLogger -from typing import Coroutine, Iterable, List, Type, TypeVar, Union, _GenericAlias, get_args +from typing import Coroutine, Iterable, List, Type, TypeVar, Union, get_args + +try: + from typing import _GenericAlias + +except ImportError: + from typing import _BaseGenericAlias as _GenericAlias from ..api.error import LibraryException from ..api.http.client import HTTPClient @@ -26,6 +32,9 @@ class Force(str, Enum): """ An enum representing the force methods for the get method + + :ivar str CACHE: Enforce the usage of cache and block the usage of http + :ivar str HTTP: Enforce the usage of http and block the usage of cache """ CACHE = "cache" diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index ca03d7bdb..7bf319f70 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -27,13 +27,13 @@ def get( # API-object related -#with http force +# with http force # single objects @overload def get(client: Client, obj: Type[Channel], *, channel_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Channel]: ... @overload def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force: Optional[Literal["http", Force.HTTP]] = None + client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id : int, force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[Emoji]: ... @overload def get(client: Client, obj: Type[Guild], *, guild_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Guild]: ... From 7c25ab3564191de837ca652f95cc70773e1f43a0 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 20:27:39 +0200 Subject: [PATCH 18/53] feat: more work on get --- docs/api.rst | 1 + docs/get.rst | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index fd046ff0b..7c1cdc37e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -20,6 +20,7 @@ Interactions ext.rst context.rst + get.rst .. toctree:: :maxdepth: 2 diff --git a/docs/get.rst b/docs/get.rst index 245fc022d..5553f04c5 100644 --- a/docs/get.rst +++ b/docs/get.rst @@ -1,7 +1,7 @@ .. currentmodule:: interactions -Bot Client -========== +The `get`-Method +================ .. automodule:: interactions.client.get :members: From f8a8305023704476dd1c0af377357afa80b7ad18 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 22:02:23 +0200 Subject: [PATCH 19/53] feat: more work on get --- docs/get.rst | 2 +- interactions/client/get.py | 176 ++++++++++++++++++++++++------------- 2 files changed, 114 insertions(+), 64 deletions(-) diff --git a/docs/get.rst b/docs/get.rst index 5553f04c5..fed92a467 100644 --- a/docs/get.rst +++ b/docs/get.rst @@ -1,6 +1,6 @@ .. currentmodule:: interactions -The `get`-Method +The ``get``-Method ================ .. automodule:: interactions.client.get diff --git a/interactions/client/get.py b/interactions/client/get.py index 9682bcf12..8708b1bc8 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -45,10 +45,116 @@ def get(*args, **kwargs): """ Helper method to get an object. + This method can do the following: + * Get a list of a specific objects + * purely from cache + * purely from HTTP + * from cache and additionally from HTTP for every ID that was not found in cache + * Get a single specific object + * purely from cache + * purely from HTTP + * from HTTP if not found in cache else from cache + * Get an object from an iterable + * based of its name + * based of its ID + * based of a custom check + + The method has to be awaited when: + * You don't force anything + * You force HTTP + The method has not to be awaited when: + * You get from an iterable + * You force cache + + .. note :: + Technically, there is no need for an `await` if there is an object found in the cache. Because of the fact, + that as long as you don't enforce the cache the function will get the object from HTTP, if it is not in the + cache, you still have to await it. This has been done to reduce confusion on whether the object origins from + an HTTP call or a cache result and to remove the extra step for you, to check if the returned object is an + awaitable or not + + + Forcing: + Forcing can be done via the `force` keyword argument. + * `force="cache"` or `force=interactions.Force.CACHE`: + This forces the method to only return from cache (if the object is not found it will return `None`). If + you use this, you don't need to await the method + """ + + def get_cache(_object: Type[_T], _list: bool = False) -> Union[_T, List[_T]]: + nonlocal kwarg_name + + if _list: + _obj = [] + if isinstance(_object, Member): # Can't be more dynamic on this + _values = () + _guild_id = Snowflake(kwargs.get("guild_id")) + for _id in kwargs.get("member_ids"): + _values += ( + ( + _guild_id, + Snowflake(_id), + ), + ) + for item in _values: + _objects.append(client.cache[_object].get(item)) + + else: + _obj.extend( + client.cache[_object].get(Snowflake(_id), None) + for _id in kwargs.get(kwarg_name) + ) + else: + if isinstance(_object, Member): + _values = ( + Snowflake(kwargs.get("guild_id")), + Snowflake(kwargs.get("member_id")), + ) # Fuck it, I can't be dynamic on this + else: + _values = Snowflake(kwargs.get(kwarg_name)) + + _obj = client.cache[_object].get(_values) + return _obj + + def search_iterable(*args, **kwargs): + def run_check(_obj, _check): + return _check(_obj) + + item: Iterable = args[0] + if not isinstance(item, Iterable): + raise LibraryException(message="The specified item must be an iterable!", code=12) + + if not kwargs: + raise LibraryException( + message="You have to specify either the name, id or a custom check to check against!", + code=12, + ) + + if len(list(kwargs)) > 1: + raise LibraryException( + message="Only one keyword argument to check against is allowed!", code=12 + ) + + _arg = str(list(kwargs)[0]) + + __obj = next( + ( + _ + for _ in item + if ( + str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) + if not isfunction(kwargs.get(_arg)) + else run_check(item, kwargs.get(_arg)) + ) + ), + None, + ) + return __obj + if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): raise LibraryException(message="You can only use Iterables as single-argument!", code=12) @@ -65,7 +171,7 @@ def get(*args, **kwargs): http_name = f"get_{obj.__name__.lower()}" kwarg_name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias): - _obj = get_args(obj)[0] + _obj: Type[_T] = get_args(obj)[0] _objects: List[_obj] = [] kwarg_name += "s" @@ -73,28 +179,13 @@ def get(*args, **kwargs): force_http = kwargs.pop("force", None) == "http" if not force_http: - if isinstance(_obj, Member): # Can't be more dynamic on this - _values = () - _guild_id = Snowflake(kwargs.get("guild_id")) - for _id in kwargs.get("member_ids"): - _values += ( - ( - _guild_id, - Snowflake(_id), - ), - ) - for item in _values: - _objects.append(client.cache[obj].get(item)) - - else: - for _id in kwargs.get(kwarg_name): - _objects.append(client.cache[obj].get(Snowflake(_id), None)) + _objects = get_cache(_obj, _list=True) if force_cache: return _objects elif not force_http and None not in _objects: - return _cache(_objects) + return _return_cache(_objects) elif force_http: _objects.clear() @@ -123,21 +214,13 @@ def get(*args, **kwargs): force_cache = kwargs.pop("force", None) == "cache" force_http = kwargs.pop("force", None) == "http" if not force_http: - if isinstance(obj, Member): - _values = ( - Snowflake(kwargs.get("guild_id")), - Snowflake(kwargs.get("member_id")), - ) # Fuck it, I can't be dynamic on this - else: - _values = Snowflake(kwargs.get(kwarg_name)) - - _obj = client.cache[obj].get(_values) + _obj = get_cache(obj) if force_cache: return _obj elif not force_http and _obj: - return _cache(_obj) + return _return_cache(_obj) else: return _http_request( @@ -145,40 +228,7 @@ def get(*args, **kwargs): ) elif len(args) == 1: - - def run_check(_obj, _check): - return _check(_obj) - - item: Iterable = args[0] - if not isinstance(item, Iterable): - raise LibraryException(message="The specified item must be an iterable!", code=12) - - if not kwargs: - raise LibraryException( - message="You have to specify either the name, id or a custom check to check against!", - code=12, - ) - - if len(list(kwargs)) > 1: - raise LibraryException( - message="Only one keyword argument to check against is allowed!", code=12 - ) - - _arg = str(list(kwargs)[0]) - - __obj = next( - ( - _ - for _ in item - if ( - str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) - if not isfunction(kwargs.get(_arg)) - else run_check(item, kwargs.get(_arg)) - ) - ), - None, - ) - return __obj + return search_iterable(*args, **kwargs) async def _http_request( @@ -205,6 +255,6 @@ async def _http_request( return [obj(**await req, _client=http) if isawaitable(req) else req for req in request] -async def _cache(obj: _T) -> _T: +async def _return_cache(obj: _T) -> _T: await sleep(0) # iirc Bluenix meant that any coroutine should await at least once return obj From 4d5afb3ea81e7ce68a9b2035932e9e60336c48cc Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 22:15:45 +0200 Subject: [PATCH 20/53] Update get.py --- interactions/client/get.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 8708b1bc8..4e3d85661 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -121,8 +121,6 @@ def get_cache(_object: Type[_T], _list: bool = False) -> Union[_T, List[_T]]: return _obj def search_iterable(*args, **kwargs): - def run_check(_obj, _check): - return _check(_obj) item: Iterable = args[0] if not isinstance(item, Iterable): @@ -148,7 +146,7 @@ def run_check(_obj, _check): if ( str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) if not isfunction(kwargs.get(_arg)) - else run_check(item, kwargs.get(_arg)) + else kwargs.get(_arg)(item) ) ), None, From f78d99f30eccaf3d93a2762fa9cb71deacf4c96d Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 23:37:21 +0200 Subject: [PATCH 21/53] Update get.py --- interactions/client/get.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 4e3d85661..022662a95 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -67,7 +67,7 @@ def get(*args, **kwargs): * You force cache .. note :: - Technically, there is no need for an `await` if there is an object found in the cache. Because of the fact, + Technically, there is no need for an ``await`` if there is an object found in the cache. Because of the fact, that as long as you don't enforce the cache the function will get the object from HTTP, if it is not in the cache, you still have to await it. This has been done to reduce confusion on whether the object origins from an HTTP call or a cache result and to remove the extra step for you, to check if the returned object is an @@ -75,13 +75,19 @@ def get(*args, **kwargs): Forcing: - Forcing can be done via the `force` keyword argument. - * `force="cache"` or `force=interactions.Force.CACHE`: + Forcing can be done via the ``force`` keyword argument. + * ``force="cache"`` or ``force=interactions.Force.CACHE``: This forces the method to only return from cache (if the object is not found it will return `None`). If - you use this, you don't need to await the method - - - + you use this, you don't need to await the method. + + * ``force="http"`` or ``force=interactions.Force.HTTP``: + This forces the method to make an HTTP request to the discord API and return the result of it. If you + use this, you have to await the method. + .. attention :: + If you are a PyCharm user, please be aware of a bug that causes incorrect suggestions to appear if + using an enum. Even if PyCharm shows a normal object as result, you have to await the method if you + enforce HTTP. To prevent this bug from happening it is suggested using ``force="http"` instead of + the enum. """ From 5eb9de15fdcc258e8d98152ecc473a752dd25c3f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Jul 2022 21:37:34 +0000 Subject: [PATCH 22/53] ci: correct from checks. --- interactions/client/get.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 022662a95..5a0d8cd71 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -78,11 +78,11 @@ def get(*args, **kwargs): Forcing can be done via the ``force`` keyword argument. * ``force="cache"`` or ``force=interactions.Force.CACHE``: This forces the method to only return from cache (if the object is not found it will return `None`). If - you use this, you don't need to await the method. + you use this, you don't need to await the method. * ``force="http"`` or ``force=interactions.Force.HTTP``: This forces the method to make an HTTP request to the discord API and return the result of it. If you - use this, you have to await the method. + use this, you have to await the method. .. attention :: If you are a PyCharm user, please be aware of a bug that causes incorrect suggestions to appear if using an enum. Even if PyCharm shows a normal object as result, you have to await the method if you From c3db5f2af4f2c8658666d6399c5bb824c69226c1 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Tue, 5 Jul 2022 23:45:29 +0200 Subject: [PATCH 23/53] Update get.py --- interactions/client/get.py | 1 + 1 file changed, 1 insertion(+) diff --git a/interactions/client/get.py b/interactions/client/get.py index 5a0d8cd71..f62a18cbe 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -83,6 +83,7 @@ def get(*args, **kwargs): * ``force="http"`` or ``force=interactions.Force.HTTP``: This forces the method to make an HTTP request to the discord API and return the result of it. If you use this, you have to await the method. + .. attention :: If you are a PyCharm user, please be aware of a bug that causes incorrect suggestions to appear if using an enum. Even if PyCharm shows a normal object as result, you have to await the method if you From 6a0559832971c0a00d37268e237af094dc6dff8f Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Wed, 6 Jul 2022 10:50:29 +0200 Subject: [PATCH 24/53] Update get.py --- interactions/client/get.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index f62a18cbe..51830d709 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -68,16 +68,16 @@ def get(*args, **kwargs): .. note :: Technically, there is no need for an ``await`` if there is an object found in the cache. Because of the fact, - that as long as you don't enforce the cache the function will get the object from HTTP, if it is not in the + that, as long as you don't enforce the cache, the function will get the object from HTTP, if it is not in the cache, you still have to await it. This has been done to reduce confusion on whether the object origins from - an HTTP call or a cache result and to remove the extra step for you, to check if the returned object is an - awaitable or not + an HTTP call or a cache result and to remove the extra step for you to check if the returned object is an + awaitable or not. Forcing: Forcing can be done via the ``force`` keyword argument. * ``force="cache"`` or ``force=interactions.Force.CACHE``: - This forces the method to only return from cache (if the object is not found it will return `None`). If + This forces the method to only return from cache (if the object is not found it will return ``None``). If you use this, you don't need to await the method. * ``force="http"`` or ``force=interactions.Force.HTTP``: @@ -87,7 +87,7 @@ def get(*args, **kwargs): .. attention :: If you are a PyCharm user, please be aware of a bug that causes incorrect suggestions to appear if using an enum. Even if PyCharm shows a normal object as result, you have to await the method if you - enforce HTTP. To prevent this bug from happening it is suggested using ``force="http"` instead of + enforce HTTP. To prevent this bug from happening it is suggested using ``force="http"`` instead of the enum. """ From e57e79bd388499a3c744a0f6b60a0b6e0d1b3ad8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 Jul 2022 08:50:44 +0000 Subject: [PATCH 25/53] ci: correct from checks. --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 51830d709..a2d7492da 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -71,7 +71,7 @@ def get(*args, **kwargs): that, as long as you don't enforce the cache, the function will get the object from HTTP, if it is not in the cache, you still have to await it. This has been done to reduce confusion on whether the object origins from an HTTP call or a cache result and to remove the extra step for you to check if the returned object is an - awaitable or not. + awaitable or not. Forcing: From d5b7a35b46221a193e6e86bd6f9ec7f837b04e21 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:20:49 +0200 Subject: [PATCH 26/53] Update get.py --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index a2d7492da..a46614d5d 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -153,7 +153,7 @@ def search_iterable(*args, **kwargs): if ( str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) if not isfunction(kwargs.get(_arg)) - else kwargs.get(_arg)(item) + else kwargs.get(_arg)(_) ) ), None, From 6da4c45c3e4605335ce70eb4569f7d4a1bfe1053 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:48:25 +0200 Subject: [PATCH 27/53] feat: more work on get --- interactions/client/get.py | 186 ++++++++++++++++++++---------------- interactions/client/get.pyi | 152 +++++++++-------------------- 2 files changed, 151 insertions(+), 187 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index a46614d5d..756c89583 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -2,7 +2,7 @@ from enum import Enum from inspect import isawaitable, isfunction from logging import getLogger -from typing import Coroutine, Iterable, List, Type, TypeVar, Union, get_args +from typing import Coroutine, Iterable, List, Optional, Type, TypeVar, Union, get_args try: from typing import _GenericAlias @@ -12,16 +12,20 @@ from ..api.error import LibraryException from ..api.http.client import HTTPClient +from ..api.models.channel import Channel from ..api.models.guild import Guild from ..api.models.member import Member -from ..api.models.message import Emoji +from ..api.models.message import Emoji, Message, Sticker from ..api.models.misc import Snowflake from ..api.models.role import Role +from ..api.models.user import User +from ..api.models.webhook import Webhook from .bot import Client log = getLogger("get") -_T = TypeVar("_T") +_A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) +# can be none because cache __all__ = ( "get", @@ -31,7 +35,7 @@ class Force(str, Enum): """ - An enum representing the force methods for the get method + An enum representing the force types for the get method. :ivar str CACHE: Enforce the usage of cache and block the usage of http :ivar str HTTP: Enforce the usage of http and block the usage of cache @@ -92,80 +96,31 @@ def get(*args, **kwargs): """ - def get_cache(_object: Type[_T], _list: bool = False) -> Union[_T, List[_T]]: - nonlocal kwarg_name - - if _list: - _obj = [] - if isinstance(_object, Member): # Can't be more dynamic on this - _values = () - _guild_id = Snowflake(kwargs.get("guild_id")) - for _id in kwargs.get("member_ids"): - _values += ( - ( - _guild_id, - Snowflake(_id), - ), - ) - for item in _values: - _objects.append(client.cache[_object].get(item)) + def resolve_kwargs(): + # This function is needed to get correct kwarg names + nonlocal kwargs + if __id := kwargs.pop("guild_or_channel_id", None): + kwargs[f"{'channel_id' if obj in [Message, List[Message]] else 'guild_id'}"] = __id - else: - _obj.extend( - client.cache[_object].get(Snowflake(_id), None) - for _id in kwargs.get(kwarg_name) - ) - else: - if isinstance(_object, Member): - _values = ( - Snowflake(kwargs.get("guild_id")), - Snowflake(kwargs.get("member_id")), - ) # Fuck it, I can't be dynamic on this - else: - _values = Snowflake(kwargs.get(kwarg_name)) - - _obj = client.cache[_object].get(_values) - return _obj - - def search_iterable(*args, **kwargs): - - item: Iterable = args[0] - if not isinstance(item, Iterable): - raise LibraryException(message="The specified item must be an iterable!", code=12) - - if not kwargs: - raise LibraryException( - message="You have to specify either the name, id or a custom check to check against!", - code=12, - ) + if __id := kwargs.pop("object_id", None): + _kwarg_name = f"{obj.__name__.lower()}_id" + kwargs[_kwarg_name] = __id - if len(list(kwargs)) > 1: - raise LibraryException( - message="Only one keyword argument to check against is allowed!", code=12 - ) + elif __id := kwargs.pop("object_ids", None): + _kwarg_name = f"{get_args(obj)[0].__name__.lower()}_ids" + kwargs[_kwarg_name] = __id - _arg = str(list(kwargs)[0]) - - __obj = next( - ( - _ - for _ in item - if ( - str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) - if not isfunction(kwargs.get(_arg)) - else kwargs.get(_arg)(_) - ) - ), - None, - ) - return __obj + else: + raise LibraryException(code=12, message="The specified kwargs are invalid!") if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): raise LibraryException(message="You can only use Iterables as single-argument!", code=12) + resolve_kwargs() + if len(args) == 2: client: Client - obj: Union[Type[_T], Type[List[_T]]] + obj: Union[Type[_A], Type[List[_A]]] client, obj = args if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): @@ -176,7 +131,7 @@ def search_iterable(*args, **kwargs): http_name = f"get_{obj.__name__.lower()}" kwarg_name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias): - _obj: Type[_T] = get_args(obj)[0] + _obj: Type[_A] = get_args(obj)[0] _objects: List[_obj] = [] kwarg_name += "s" @@ -184,7 +139,7 @@ def search_iterable(*args, **kwargs): force_http = kwargs.pop("force", None) == "http" if not force_http: - _objects = get_cache(_obj, _list=True) + _objects = _get_cache(_obj, client, kwarg_name, _list=True, **kwargs) if force_cache: return _objects @@ -214,12 +169,12 @@ def search_iterable(*args, **kwargs): _objects[_index] = _request return _http_request(_obj, http=client._http, request=_objects) - _obj: _T = None + _obj: Optional[_A] = None force_cache = kwargs.pop("force", None) == "cache" force_http = kwargs.pop("force", None) == "http" if not force_http: - _obj = get_cache(obj) + _obj = _get_cache(obj, client, kwarg_name, **kwargs) if force_cache: return _obj @@ -228,21 +183,19 @@ def search_iterable(*args, **kwargs): return _return_cache(_obj) else: - return _http_request( - obj=obj, http=client._http, request=None, _name=http_name, **kwargs - ) + return _http_request(obj=obj, http=client._http, _name=http_name, **kwargs) elif len(args) == 1: - return search_iterable(*args, **kwargs) + return _search_iterable(*args, **kwargs) async def _http_request( - obj: Type[_T], + obj: Type[_A], http: HTTPClient, - request: Union[Coroutine, List[Union[_T, Coroutine]], List[Coroutine]] = None, + request: Union[Coroutine, List[Union[_A, Coroutine]], List[Coroutine]] = None, _name: str = None, **kwargs, -) -> Union[_T, List[_T]]: +) -> Union[_A, List[_A]]: if not request: if obj in (Role, Emoji): @@ -260,6 +213,77 @@ async def _http_request( return [obj(**await req, _client=http) if isawaitable(req) else req for req in request] -async def _return_cache(obj: _T) -> _T: +async def _return_cache( + obj: Union[Optional[_A], List[Optional[_A]]] +) -> Union[Optional[_A], List[Optional[_A]]]: await sleep(0) # iirc Bluenix meant that any coroutine should await at least once return obj + + +def _get_cache( + _object: Type[_A], client: Client, kwarg_name: str, _list: bool = False, **kwargs +) -> Union[Optional[_A], List[Optional[_A]]]: + + if _list: + _obj = [] + if isinstance(_object, Member): # Can't be more dynamic on this + _values = () + _guild_id = Snowflake(kwargs.get("guild_id")) + for _id in kwargs.get("member_ids"): + _values += ( + ( + _guild_id, + Snowflake(_id), + ), + ) + _obj.extend(client.cache[_object].get(item) for item in _values) + + else: + _obj.extend( + client.cache[_object].get(Snowflake(_id), None) for _id in kwargs.get(kwarg_name) + ) + else: + if isinstance(_object, Member): + _values = ( + Snowflake(kwargs.get("guild_id")), + Snowflake(kwargs.get("member_id")), + ) # Fuck it, I can't be dynamic on this + else: + _values = Snowflake(kwargs.get(kwarg_name)) + + _obj = client.cache[_object].get(_values) + return _obj + + +def _search_iterable(*args, **kwargs) -> Optional[_A]: + + item: Iterable = args[0] + if not isinstance(item, Iterable): + raise LibraryException(message="The specified item must be an iterable!", code=12) + + if not kwargs: + raise LibraryException( + message="You have to specify either the name, id or a custom check to check against!", + code=12, + ) + + if len(list(kwargs)) > 1: + raise LibraryException( + message="Only one keyword argument to check against is allowed!", code=12 + ) + + _arg = str(list(kwargs)[0]) + + __obj = next( + ( + _ + for _ in item + if ( + str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) + if not isfunction(kwargs.get(_arg)) + else kwargs.get(_arg)(item) + ) + ), + None, + ) + return __obj diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 7bf319f70..bd465406c 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -2,15 +2,20 @@ from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, from interactions.client.bot import Client from enum import Enum -from interactions.api.models.channel import Channel -from interactions.api.models.guild import Guild -from interactions.api.models.member import Member -from interactions.api.models.message import Message, Emoji, Sticker -from interactions.api.models.role import Role -from interactions.api.models.user import User -from interactions.api.models.webhook import Webhook +from ..api.models.channel import Channel +from ..api.models.guild import Guild +from ..api.models.member import Member +from ..api.models.message import Message, Emoji, Sticker +from ..api.models.user import User +from ..api.models.webhook import Webhook +from ..api.models.role import Role -_T = TypeVar("_T") +_T = TypeVar("_T", Channel, Guild, Webhook, User, Sticker) +_P = TypeVar("_P", Member, Emoji, Role, Message) +_A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) +# can be none because cache + +__all__: tuple class Force(str, Enum): """ @@ -22,141 +27,76 @@ class Force(str, Enum): # not API-object related @overload def get( - item: Iterable, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool] -) -> _T: ... + item: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool] +) -> Optional[_A]: ... # API-object related # with http force # single objects @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Channel]: ... -@overload -def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id : int, force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[Emoji]: ... -@overload -def get(client: Client, obj: Type[Guild], *, guild_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Guild]: ... -@overload def get( - client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[Member]: ... + client: Client, + obj: Type[_T], + *, + object_id: int, + force: Optional[Literal["http", Force.HTTP]] = None +) -> Awaitable[_T]: ... + @overload def get( client: Client, - obj: Type[Message], + obj: Type[_P], *, - channel_id: int, - message_id: int, - force: Optional[Literal["http"]] = None, -) -> Awaitable[Message]: ... -@overload -def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Role]: ... -@overload -def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Sticker]: ... -@overload -def get(client: Client, obj: Type[User], *, user_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[User]: ... -@overload -def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[Webhook]: ... + guild_or_channel_id: int, + object_id: int, + force: Optional[Literal["http", Force.HTTP]] = None +) -> Awaitable[_P]: ... # list of objects @overload -def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Channel]]: ... -@overload -def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Emoji]]: ... -@overload -def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Guild]]: ... -@overload def get( client: Client, - obj: Type[List[Member]], + obj: Type[List[_T]], *, - guild_id: int, - member_ids: List[int], + object_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[List[Member]]: ... +) -> Awaitable[List[_T]]: ... + @overload def get( client: Client, - obj: Type[List[Message]], + obj: Type[List[_P]], *, - channel_id: int, - message_ids: List[int], + guild_or_channel_id: int, + object_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[List[Message]]: ... -@overload -def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Role]]: ... -@overload -def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Sticker]]: ... -@overload -def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[User]]: ... -@overload -def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None) -> Awaitable[List[Webhook]]: ... +) -> Awaitable[List[_P]]: ... # with cache force @overload -def get(client: Client, obj: Type[Channel], *, channel_id: int, force: Literal["cache", Force.CACHE]) -> Channel: ... -@overload -def get( - client: Client, obj: Type[Emoji], *, guild_id: int, emoji_id: int, force: Literal["cache", Force.CACHE] -) -> Emoji: ... -@overload -def get(client: Client, obj: Type[Guild], *, guild_id: int, force: Literal["cache", Force.CACHE]) -> Guild: ... -@overload -def get( - client: Client, obj: Type[Member], *, guild_id: int, member_id: int, force: Literal["cache", Force.CACHE] -) -> Member: ... +def get(client: Client, obj: Type[_T], *, object_id: int, force: Literal["cache", Force.CACHE]) -> Optional[_T]: ... + @overload def get( - client: Client, - obj: Type[Message], - *, - channel_id: int, - message_id: int, - force: Literal["cache", Force.CACHE], -) -> Message: ... -@overload -def get(client: Client, obj: Type[Role], *, guild_id: int, role_id: int, force: Literal["cache", Force.CACHE]) -> Role: ... -@overload -def get(client: Client, obj: Type[Sticker], *, sticker_id: int, force: Literal["cache", Force.CACHE]) -> Sticker: ... -@overload -def get(client: Client, obj: Type[User], *, user_id: int, force: Literal["cache", Force.CACHE]) -> User: ... -@overload -def get(client: Client, obj: Type[Webhook], *, webhook_id: int, force: Literal["cache", Force.CACHE]) -> Webhook: ... + client: Client, obj: Type[_P], *, guild_or_channel_id: int, object_id: int, force: Literal["cache", Force.CACHE] +) -> Optional[_P]: ... # list of objects @overload -def get(client: Client, obj: Type[List[Channel]], *, channel_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Channel]: ... -@overload -def get(client: Client, obj: Type[List[Emoji]], *, guild_id: int, emoji_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Emoji]: ... -@overload -def get(client: Client, obj: Type[List[Guild]], *, guild_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Guild]: ... -@overload def get( - client: Client, - obj: Type[List[Member]], - *, - guild_id: int, - member_ids: List[int], - force: Literal["cache", Force.CACHE] -) -> List[Member]: ... + client: Client, obj: Type[List[_T]], *, object_ids: List[int], force: Literal["cache", Force.CACHE] +) -> List[Optional[_T]]: ... + @overload def get( client: Client, - obj: Type[List[Message]], + obj: Type[List[_P]], *, - channel_id: int, - message_ids: List[int], + guild_or_channel_id: int, + object_ids: List[int], force: Literal["cache", Force.CACHE] -) -> List[Message]: ... -@overload -def get(client: Client, obj: Type[List[Role]], *, guild_id: int, role_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Role]: ... -@overload -def get(client: Client, obj: Type[List[Sticker]], *, sticker_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Sticker]: ... -@overload -def get(client: Client, obj: Type[List[User]], *, user_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[User]: ... -@overload -def get(client: Client, obj: Type[List[Webhook]], *, webhook_ids: List[int], force: Literal["cache", Force.CACHE]) -> List[Webhook]: ... +) -> List[Optional[_P]]: ... # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, # so we'll leave that out From fc5ee69a11f45b7fff4cdca9b48cc16c3a6f3e5e Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:09:40 +0200 Subject: [PATCH 28/53] docs: work on docs for get --- interactions/client/get.py | 133 ++++++++++++++++++++++++++++++------ interactions/client/get.pyi | 11 ++- 2 files changed, 117 insertions(+), 27 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 756c89583..276bb880a 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -25,7 +25,6 @@ log = getLogger("get") _A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) -# can be none because cache __all__ = ( "get", @@ -62,6 +61,7 @@ def get(*args, **kwargs): * based of its name * based of its ID * based of a custom check + * based of any other attribute the object inside the iterable has The method has to be awaited when: * You don't force anything @@ -94,45 +94,117 @@ def get(*args, **kwargs): enforce HTTP. To prevent this bug from happening it is suggested using ``force="http"`` instead of the enum. - """ + Getting from an iterable: - def resolve_kwargs(): - # This function is needed to get correct kwarg names - nonlocal kwargs - if __id := kwargs.pop("guild_or_channel_id", None): - kwargs[f"{'channel_id' if obj in [Message, List[Message]] else 'guild_id'}"] = __id + .. code-block:: python - if __id := kwargs.pop("object_id", None): - _kwarg_name = f"{obj.__name__.lower()}_id" - kwargs[_kwarg_name] = __id + # Getting an object from an iterable + check = lambda role: return role.name == "ADMIN" and role.color == 0xff0000 + roles = [ + interactions.Role(name="NOT ADMIN", color=0xff0000), + interactions.Role(name="ADMIN", color=0xff0000), + ] + role = get(roles, check=check) + # role will be `interactions.Role(name="ADMIN", color=0xff0000)` - elif __id := kwargs.pop("object_ids", None): - _kwarg_name = f"{get_args(obj)[0].__name__.lower()}_ids" - kwargs[_kwarg_name] = __id + You can specify *any* attribute to check that the object could have (although only ``check``, ``id`` and + ``name`` are type-hinted) and the method will check for a match. - else: - raise LibraryException(code=12, message="The specified kwargs are invalid!") + Getting an object: + + Here you will see two examples on how to get a single objects and the variations of how the object can be + gotten. + + * Example 1/2: Getting a Channel: + + .. code-block:: python + + # normally + channel = await get(client, interactions.Channel, object_id=your_channel_id) + # always has a value + + # with http force + channel = await get(client, interactions.Channel, object_id=your_channel_id, force="http") + # always has a value + + # with cache force + channel = get(client, interactions.Channel, object_id=your_channel_id, force="cache") + # because of cache only, this can be None + + + * Example 2/2: Getting a Member: + + .. code-block:: python + + # normally + member = await get( + client, interactions.Member, parent_id=your_guild_id, object_id=your_member_id + ) + # always has a value + + # with http force + member = await get( + client, interactions.Member, parent_id=your_guild_id, object_id=your_member_id + ) + # always has a value + + # with cache force + member = await get( + client, interactions.Member, parent_id=your_guild_id, object_id=your_member_id + ) + # because of cache only, this can be None + + + Both examples should have given you a basic overview on how to get a single object. Now we will move on with + lists of objects. + + .. important:: + The ``parent_id`` represents the channel or guild id that belongs to the objects you want to get. It is + called ``parent_id`` bcause ``guild_or_channel_id`` would be horrible to type out every time. + + Getting a list of an object: + Here you will see 1 example of how to get a list of objects. The possibilities on how to force (and their + results) are the same as in the examples above. + + * Example 1/1: Getting a list of members: + + .. code-block:: python + + from typing import List # this is required + members = await get( + client, + List[interactions.Member], + parent_id=your_guild_id, + object_ids=[your_member_id1, your_member_id2, ...], + ) + + + If you enforce cache when getting a list of objects, found objets will be placed into the list and not found + objects will be placed as ``None`` into the list. + + + + """ if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): raise LibraryException(message="You can only use Iterables as single-argument!", code=12) - resolve_kwargs() - if len(args) == 2: - client: Client - obj: Union[Type[_A], Type[List[_A]]] client, obj = args if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): + client: Client + obj: Union[Type[_A], Type[List[_A]]] raise LibraryException( message="The object must not be an instance of a class!", code=12 ) + kwargs = _resolve_kwargs(obj, **kwargs) http_name = f"get_{obj.__name__.lower()}" kwarg_name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias): _obj: Type[_A] = get_args(obj)[0] - _objects: List[_obj] = [] + _objects: List[Union[_obj, Coroutine]] = [] kwarg_name += "s" force_cache = kwargs.pop("force", None) == "cache" @@ -263,7 +335,7 @@ def _search_iterable(*args, **kwargs) -> Optional[_A]: if not kwargs: raise LibraryException( - message="You have to specify either the name, id or a custom check to check against!", + message="You have to specify either a custom check or a keyword argument to check against!", code=12, ) @@ -287,3 +359,22 @@ def _search_iterable(*args, **kwargs) -> Optional[_A]: None, ) return __obj + + +def _resolve_kwargs(obj, **kwargs): + # This function is needed to get correct kwarg names + if __id := kwargs.pop("parent_id", None): + kwargs[f"{'channel_id' if obj in [Message, List[Message]] else 'guild_id'}"] = __id + + if __id := kwargs.pop("object_id", None): + _kwarg_name = f"{obj.__name__.lower()}_id" + kwargs[_kwarg_name] = __id + + elif __id := kwargs.pop("object_ids", None): + _kwarg_name = f"{get_args(obj)[0].__name__.lower()}_ids" + kwargs[_kwarg_name] = __id + + else: + raise LibraryException(code=12, message="The specified kwargs are invalid!") + + return kwargs diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index bd465406c..7762fe9e9 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -13,7 +13,6 @@ from ..api.models.role import Role _T = TypeVar("_T", Channel, Guild, Webhook, User, Sticker) _P = TypeVar("_P", Member, Emoji, Role, Message) _A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) -# can be none because cache __all__: tuple @@ -27,7 +26,7 @@ class Force(str, Enum): # not API-object related @overload def get( - item: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool] + item: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool], **kwargs ) -> Optional[_A]: ... # API-object related @@ -48,7 +47,7 @@ def get( client: Client, obj: Type[_P], *, - guild_or_channel_id: int, + parent_id: int, object_id: int, force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[_P]: ... @@ -68,7 +67,7 @@ def get( client: Client, obj: Type[List[_P]], *, - guild_or_channel_id: int, + parent_id: int, object_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None ) -> Awaitable[List[_P]]: ... @@ -79,7 +78,7 @@ def get(client: Client, obj: Type[_T], *, object_id: int, force: Literal["cache" @overload def get( - client: Client, obj: Type[_P], *, guild_or_channel_id: int, object_id: int, force: Literal["cache", Force.CACHE] + client: Client, obj: Type[_P], *, parent_id: int, object_id: int, force: Literal["cache", Force.CACHE] ) -> Optional[_P]: ... # list of objects @@ -93,7 +92,7 @@ def get( client: Client, obj: Type[List[_P]], *, - guild_or_channel_id: int, + parent_id: int, object_ids: List[int], force: Literal["cache", Force.CACHE] ) -> List[Optional[_P]]: ... From ec5f3047b8abb3c192cc5c3f465e301d5296a136 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:22:44 +0200 Subject: [PATCH 29/53] Update get.py --- interactions/client/get.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 276bb880a..41fc1e3b1 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -66,7 +66,7 @@ def get(*args, **kwargs): The method has to be awaited when: * You don't force anything * You force HTTP - The method has not to be awaited when: + The method hasn't to be awaited when: * You get from an iterable * You force cache @@ -329,7 +329,7 @@ def _get_cache( def _search_iterable(*args, **kwargs) -> Optional[_A]: - item: Iterable = args[0] + items: Iterable = args[0] if not isinstance(item, Iterable): raise LibraryException(message="The specified item must be an iterable!", code=12) @@ -348,10 +348,10 @@ def _search_iterable(*args, **kwargs) -> Optional[_A]: __obj = next( ( - _ - for _ in item + item + for item in items if ( - str(getattr(_, _arg, None)) == str(kwargs.get(_arg)) + str(getattr(item, _arg, None)) == str(kwargs.get(_arg)) if not isfunction(kwargs.get(_arg)) else kwargs.get(_arg)(item) ) From 37798e511e55771436fabadbf137ed22ecc90202 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:25:55 +0200 Subject: [PATCH 30/53] Update get.pyi --- interactions/client/get.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 7762fe9e9..7098083f7 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -26,7 +26,7 @@ class Force(str, Enum): # not API-object related @overload def get( - item: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool], **kwargs + items: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool], **kwargs ) -> Optional[_A]: ... # API-object related From aaceae5f4a5fd6dee697887afbbb1eead1676105 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:26:47 +0200 Subject: [PATCH 31/53] Update get.py --- interactions/client/get.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 41fc1e3b1..e58ca60dc 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -327,9 +327,8 @@ def _get_cache( return _obj -def _search_iterable(*args, **kwargs) -> Optional[_A]: +def _search_iterable(item: Iterable[_A] , **kwargs) -> Optional[_A]: - items: Iterable = args[0] if not isinstance(item, Iterable): raise LibraryException(message="The specified item must be an iterable!", code=12) From 00a61906d8f21016c90f5677ac250ffab543c758 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:27:04 +0000 Subject: [PATCH 32/53] ci: correct from checks. --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index e58ca60dc..82be5a458 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -327,7 +327,7 @@ def _get_cache( return _obj -def _search_iterable(item: Iterable[_A] , **kwargs) -> Optional[_A]: +def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]: if not isinstance(item, Iterable): raise LibraryException(message="The specified item must be an iterable!", code=12) From 1ef59278613becccc27f5720b3beeb5fb848a5a3 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:30:09 +0200 Subject: [PATCH 33/53] Update get.pyi --- interactions/client/get.pyi | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 7098083f7..3819810ce 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -99,3 +99,19 @@ def get( # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, # so we'll leave that out + +def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]:... +def _get_cache( + _object: Type[_A], client: Client, kwarg_name: str, _list: bool = False, **kwargs +) -> Union[Optional[_A], List[Optional[_A]]]:... +async def _return_cache( + obj: Union[Optional[_A], List[Optional[_A]]] +) -> Union[Optional[_A], List[Optional[_A]]]:... +async def _http_request( + obj: Type[_A], + http: HTTPClient, + request: Union[Coroutine, List[Union[_A, Coroutine]], List[Coroutine]] = None, + _name: str = None, + **kwargs, +) -> Union[_A, List[_A]]:... + From 2b83de11d642ca73a9864992e484fe0ce1932ecb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 20:30:22 +0000 Subject: [PATCH 34/53] ci: correct from checks. --- interactions/client/get.pyi | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index 3819810ce..d7e84e026 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -100,18 +100,17 @@ def get( # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, # so we'll leave that out -def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]:... +def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]:... def _get_cache( _object: Type[_A], client: Client, kwarg_name: str, _list: bool = False, **kwargs -) -> Union[Optional[_A], List[Optional[_A]]]:... +) -> Union[Optional[_A], List[Optional[_A]]]:... async def _return_cache( obj: Union[Optional[_A], List[Optional[_A]]] -) -> Union[Optional[_A], List[Optional[_A]]]:... +) -> Union[Optional[_A], List[Optional[_A]]]:... async def _http_request( obj: Type[_A], http: HTTPClient, request: Union[Coroutine, List[Union[_A, Coroutine]], List[Coroutine]] = None, _name: str = None, **kwargs, -) -> Union[_A, List[_A]]:... - +) -> Union[_A, List[_A]]:... From 51372d26c3dc2d74f736fa58b2bf2ff3059bc188 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sat, 9 Jul 2022 22:50:13 +0200 Subject: [PATCH 35/53] Update get.py --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 82be5a458..316f13406 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -66,7 +66,7 @@ def get(*args, **kwargs): The method has to be awaited when: * You don't force anything * You force HTTP - The method hasn't to be awaited when: + The method doesn't have to be awaited when: * You get from an iterable * You force cache From 9b150c7a7117fe57f09d0e1dff8311b80c545f80 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Sun, 10 Jul 2022 20:11:11 +0200 Subject: [PATCH 36/53] feat: Allow `list[obj]` --- interactions/client/get.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 316f13406..4af1bd270 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -10,6 +10,8 @@ except ImportError: from typing import _BaseGenericAlias as _GenericAlias +from sys import version_info + from ..api.error import LibraryException from ..api.http.client import HTTPClient from ..api.models.channel import Channel @@ -170,7 +172,10 @@ def get(*args, **kwargs): .. code-block:: python - from typing import List # this is required + from typing import List + + # you can also use `list[interactions.Member]` if you have python >= 3.9 + members = await get( client, List[interactions.Member], @@ -186,6 +191,16 @@ def get(*args, **kwargs): """ + if version_info >= (3, 9): + + def _check(): + return obj == list[get_args(obj)[0]] + + else: + + def _check(): + return False + if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): raise LibraryException(message="You can only use Iterables as single-argument!", code=12) @@ -202,7 +217,7 @@ def get(*args, **kwargs): kwargs = _resolve_kwargs(obj, **kwargs) http_name = f"get_{obj.__name__.lower()}" kwarg_name = f"{obj.__name__.lower()}_id" - if isinstance(obj, _GenericAlias): + if isinstance(obj, _GenericAlias) or _check(): _obj: Type[_A] = get_args(obj)[0] _objects: List[Union[_obj, Coroutine]] = [] kwarg_name += "s" @@ -327,10 +342,10 @@ def _get_cache( return _obj -def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]: +def _search_iterable(items: Iterable[_A], **kwargs) -> Optional[_A]: - if not isinstance(item, Iterable): - raise LibraryException(message="The specified item must be an iterable!", code=12) + if not isinstance(items, Iterable): + raise LibraryException(message="The specified items must be an iterable!", code=12) if not kwargs: raise LibraryException( From c214ff8c1652f186948dcfda1ad9b9ccc44f7683 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 07:32:50 +0200 Subject: [PATCH 37/53] Update docs/get.rst Co-authored-by: Sofia <41456914+ffl0w@users.noreply.github.com> --- docs/get.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get.rst b/docs/get.rst index fed92a467..a05c4f3ec 100644 --- a/docs/get.rst +++ b/docs/get.rst @@ -1,6 +1,6 @@ .. currentmodule:: interactions -The ``get``-Method +The ``get`` utility method ================ .. automodule:: interactions.client.get From 3c78c4d66adcab52119d0f209bf72cfc10b1c4ae Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 07:33:04 +0200 Subject: [PATCH 38/53] Update interactions/client/get.py Co-authored-by: Sofia <41456914+ffl0w@users.noreply.github.com> --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 4af1bd270..73b3aab30 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -36,7 +36,7 @@ class Force(str, Enum): """ - An enum representing the force types for the get method. + An enumerable object representing the force types for the get method. :ivar str CACHE: Enforce the usage of cache and block the usage of http :ivar str HTTP: Enforce the usage of http and block the usage of cache From 9a5c708573fe0a26c9e38d58b244a0d0ac0e79fe Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 08:51:40 +0200 Subject: [PATCH 39/53] fix: Fix `TpyeVar`s --- interactions/client/get.py | 29 ++++++++---------- interactions/client/get.pyi | 59 +++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 4af1bd270..866a5a9a1 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -14,19 +14,16 @@ from ..api.error import LibraryException from ..api.http.client import HTTPClient -from ..api.models.channel import Channel from ..api.models.guild import Guild from ..api.models.member import Member -from ..api.models.message import Emoji, Message, Sticker +from ..api.models.message import Emoji, Message from ..api.models.misc import Snowflake from ..api.models.role import Role -from ..api.models.user import User -from ..api.models.webhook import Webhook from .bot import Client log = getLogger("get") -_A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) +_T = TypeVar("_T") __all__ = ( "get", @@ -209,7 +206,7 @@ def _check(): client, obj = args if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): client: Client - obj: Union[Type[_A], Type[List[_A]]] + obj: Union[Type[_T], Type[List[_T]]] raise LibraryException( message="The object must not be an instance of a class!", code=12 ) @@ -218,7 +215,7 @@ def _check(): http_name = f"get_{obj.__name__.lower()}" kwarg_name = f"{obj.__name__.lower()}_id" if isinstance(obj, _GenericAlias) or _check(): - _obj: Type[_A] = get_args(obj)[0] + _obj: Type[_T] = get_args(obj)[0] _objects: List[Union[_obj, Coroutine]] = [] kwarg_name += "s" @@ -256,7 +253,7 @@ def _check(): _objects[_index] = _request return _http_request(_obj, http=client._http, request=_objects) - _obj: Optional[_A] = None + _obj: Optional[_T] = None force_cache = kwargs.pop("force", None) == "cache" force_http = kwargs.pop("force", None) == "http" @@ -277,12 +274,12 @@ def _check(): async def _http_request( - obj: Type[_A], + obj: Type[_T], http: HTTPClient, - request: Union[Coroutine, List[Union[_A, Coroutine]], List[Coroutine]] = None, + request: Union[Coroutine, List[Union[_T, Coroutine]], List[Coroutine]] = None, _name: str = None, **kwargs, -) -> Union[_A, List[_A]]: +) -> Union[_T, List[_T]]: if not request: if obj in (Role, Emoji): @@ -301,15 +298,15 @@ async def _http_request( async def _return_cache( - obj: Union[Optional[_A], List[Optional[_A]]] -) -> Union[Optional[_A], List[Optional[_A]]]: + obj: Union[Optional[_T], List[Optional[_T]]] +) -> Union[Optional[_T], List[Optional[_T]]]: await sleep(0) # iirc Bluenix meant that any coroutine should await at least once return obj def _get_cache( - _object: Type[_A], client: Client, kwarg_name: str, _list: bool = False, **kwargs -) -> Union[Optional[_A], List[Optional[_A]]]: + _object: Type[_T], client: Client, kwarg_name: str, _list: bool = False, **kwargs +) -> Union[Optional[_T], List[Optional[_T]]]: if _list: _obj = [] @@ -342,7 +339,7 @@ def _get_cache( return _obj -def _search_iterable(items: Iterable[_A], **kwargs) -> Optional[_A]: +def _search_iterable(items: Iterable[_T], **kwargs) -> Optional[_T]: if not isinstance(items, Iterable): raise LibraryException(message="The specified items must be an iterable!", code=12) diff --git a/interactions/client/get.pyi b/interactions/client/get.pyi index d7e84e026..9064aaae6 100644 --- a/interactions/client/get.pyi +++ b/interactions/client/get.pyi @@ -1,4 +1,4 @@ -from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, Awaitable, Literal +from typing import overload, Type, TypeVar, List, Iterable, Optional, Callable, Awaitable, Literal, Coroutine, Union from interactions.client.bot import Client from enum import Enum @@ -9,10 +9,11 @@ from ..api.models.message import Message, Emoji, Sticker from ..api.models.user import User from ..api.models.webhook import Webhook from ..api.models.role import Role +from ..api.http.client import HTTPClient -_T = TypeVar("_T", Channel, Guild, Webhook, User, Sticker) -_P = TypeVar("_P", Member, Emoji, Role, Message) -_A = TypeVar("_A", Channel, Guild, Webhook, User, Sticker, Message, Emoji, Role, Message) +_SA = TypeVar("_SA", Channel, Guild, Webhook, User, Sticker) +_MA = TypeVar("_MA", Member, Emoji, Role, Message) +_T = TypeVar("_T") __all__: tuple @@ -26,8 +27,8 @@ class Force(str, Enum): # not API-object related @overload def get( - items: Iterable[_A], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool], **kwargs -) -> Optional[_A]: ... + items: Iterable[_T], /, *, id: Optional[int] = None, name: Optional[str] = None, check: Callable[..., bool], **kwargs +) -> Optional[_T]: ... # API-object related @@ -36,81 +37,81 @@ def get( @overload def get( client: Client, - obj: Type[_T], + obj: Type[_SA], *, object_id: int, force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[_T]: ... +) -> Awaitable[_SA]: ... @overload def get( client: Client, - obj: Type[_P], + obj: Type[_MA], *, parent_id: int, object_id: int, force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[_P]: ... +) -> Awaitable[_MA]: ... # list of objects @overload def get( client: Client, - obj: Type[List[_T]], + obj: Type[List[_SA]], *, object_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[List[_T]]: ... +) -> Awaitable[List[_SA]]: ... @overload def get( client: Client, - obj: Type[List[_P]], + obj: Type[List[_MA]], *, parent_id: int, object_ids: List[int], force: Optional[Literal["http", Force.HTTP]] = None -) -> Awaitable[List[_P]]: ... +) -> Awaitable[List[_MA]]: ... # with cache force @overload -def get(client: Client, obj: Type[_T], *, object_id: int, force: Literal["cache", Force.CACHE]) -> Optional[_T]: ... +def get(client: Client, obj: Type[_SA], *, object_id: int, force: Literal["cache", Force.CACHE]) -> Optional[_SA]: ... @overload def get( - client: Client, obj: Type[_P], *, parent_id: int, object_id: int, force: Literal["cache", Force.CACHE] -) -> Optional[_P]: ... + client: Client, obj: Type[_MA], *, parent_id: int, object_id: int, force: Literal["cache", Force.CACHE] +) -> Optional[_MA]: ... # list of objects @overload def get( - client: Client, obj: Type[List[_T]], *, object_ids: List[int], force: Literal["cache", Force.CACHE] -) -> List[Optional[_T]]: ... + client: Client, obj: Type[List[_SA]], *, object_ids: List[int], force: Literal["cache", Force.CACHE] +) -> List[Optional[_SA]]: ... @overload def get( client: Client, - obj: Type[List[_P]], + obj: Type[List[_MA]], *, parent_id: int, object_ids: List[int], force: Literal["cache", Force.CACHE] -) -> List[Optional[_P]]: ... +) -> List[Optional[_MA]]: ... # Having a not-overloaded definition stops showing a warning/complaint from the IDE if wrong arguments are put in, # so we'll leave that out -def _search_iterable(item: Iterable[_A], **kwargs) -> Optional[_A]:... +def _search_iterable(item: Iterable[_T], **kwargs) -> Optional[_T]:... def _get_cache( - _object: Type[_A], client: Client, kwarg_name: str, _list: bool = False, **kwargs -) -> Union[Optional[_A], List[Optional[_A]]]:... + _object: Type[_T], client: Client, kwarg_name: str, _list: bool = False, **kwargs +) -> Union[Optional[_T], List[Optional[_T]]]:... async def _return_cache( - obj: Union[Optional[_A], List[Optional[_A]]] -) -> Union[Optional[_A], List[Optional[_A]]]:... + obj: Union[Optional[_T], List[Optional[_T]]] +) -> Union[Optional[_T], List[Optional[_T]]]:... async def _http_request( - obj: Type[_A], + obj: Type[_T], http: HTTPClient, - request: Union[Coroutine, List[Union[_A, Coroutine]], List[Coroutine]] = None, + request: Union[Coroutine, List[Union[_T, Coroutine]], List[Coroutine]] = None, _name: str = None, **kwargs, -) -> Union[_A, List[_A]]:... +) -> Union[_T, List[_T]]:... From 5a201994a7afa463894814158d14fbc7a26a392a Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 08:54:52 +0200 Subject: [PATCH 40/53] fix: --- interactions/client/get.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 48ded54c5..dfbea196a 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -198,11 +198,13 @@ def _check(): def _check(): return False - if len(args) == 2 and any(isinstance(_, Iterable) for _ in args): - raise LibraryException(message="You can only use Iterables as single-argument!", code=12) - if len(args) == 2: + if any(isinstance(_, Iterable) for _ in args): + raise LibraryException( + message="You can only use Iterables as single-argument!", code=12 + ) + client, obj = args if not isinstance(obj, type) and not isinstance(obj, _GenericAlias): client: Client @@ -280,7 +282,6 @@ async def _http_request( _name: str = None, **kwargs, ) -> Union[_T, List[_T]]: - if not request: if obj in (Role, Emoji): _guild = Guild(**await http.get_guild(kwargs.pop("guild_id")), _client=http) @@ -307,7 +308,6 @@ async def _return_cache( def _get_cache( _object: Type[_T], client: Client, kwarg_name: str, _list: bool = False, **kwargs ) -> Union[Optional[_T], List[Optional[_T]]]: - if _list: _obj = [] if isinstance(_object, Member): # Can't be more dynamic on this @@ -340,7 +340,6 @@ def _get_cache( def _search_iterable(items: Iterable[_T], **kwargs) -> Optional[_T]: - if not isinstance(items, Iterable): raise LibraryException(message="The specified items must be an iterable!", code=12) From 9c66fffe2ebeddae02fea5e2bacaa328d5727928 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 09:46:09 +0200 Subject: [PATCH 41/53] fix: allow py3.9 typehints --- interactions/client/get.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index dfbea196a..402dbcd7d 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -98,7 +98,7 @@ def get(*args, **kwargs): .. code-block:: python # Getting an object from an iterable - check = lambda role: return role.name == "ADMIN" and role.color == 0xff0000 + check = lambda role: role.name == "ADMIN" and role.color == 0xff0000 roles = [ interactions.Role(name="NOT ADMIN", color=0xff0000), interactions.Role(name="ADMIN", color=0xff0000), @@ -374,7 +374,13 @@ def _search_iterable(items: Iterable[_T], **kwargs) -> Optional[_T]: def _resolve_kwargs(obj, **kwargs): # This function is needed to get correct kwarg names if __id := kwargs.pop("parent_id", None): - kwargs[f"{'channel_id' if obj in [Message, List[Message]] else 'guild_id'}"] = __id + + if version_info >= (3, 9): + _list = [Message, List[Message], list[Message]] + else: + _list = [Message, List[Message]] + + kwargs[f"{'channel_id' if obj in _list else 'guild_id'}"] = __id if __id := kwargs.pop("object_id", None): _kwarg_name = f"{obj.__name__.lower()}_id" From 50a748ecf63b7039a0bd80f518cefbb0b705989e Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 10:25:31 +0200 Subject: [PATCH 42/53] fix: fix cache attribute and instance checks --- interactions/client/get.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 402dbcd7d..9bc38d0e1 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -310,7 +310,7 @@ def _get_cache( ) -> Union[Optional[_T], List[Optional[_T]]]: if _list: _obj = [] - if isinstance(_object, Member): # Can't be more dynamic on this + if _object == Member: # Can't be more dynamic on this _values = () _guild_id = Snowflake(kwargs.get("guild_id")) for _id in kwargs.get("member_ids"): @@ -320,14 +320,15 @@ def _get_cache( Snowflake(_id), ), ) - _obj.extend(client.cache[_object].get(item) for item in _values) + _obj.extend(client._http.cache[_object].get(item) for item in _values) else: _obj.extend( - client.cache[_object].get(Snowflake(_id), None) for _id in kwargs.get(kwarg_name) + client._http.cache[_object].get(Snowflake(_id), None) + for _id in kwargs.get(kwarg_name) ) else: - if isinstance(_object, Member): + if _object == Member: _values = ( Snowflake(kwargs.get("guild_id")), Snowflake(kwargs.get("member_id")), @@ -335,7 +336,7 @@ def _get_cache( else: _values = Snowflake(kwargs.get(kwarg_name)) - _obj = client.cache[_object].get(_values) + _obj = client._http.cache[_object].get(_values) return _obj From d7159c818c9b1964bd84f519292f3899c6db7541 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 19:38:17 +0200 Subject: [PATCH 43/53] Update interactions/client/get.py Co-authored-by: Max --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 9bc38d0e1..5142e1c3c 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -48,7 +48,7 @@ def get(*args, **kwargs): Helper method to get an object. This method can do the following: - * Get a list of a specific objects + * Get a list of specific objects * purely from cache * purely from HTTP * from cache and additionally from HTTP for every ID that was not found in cache From d5a55c91e1b30493d97c6fd418a1cb29da8c8b91 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 20:08:17 +0200 Subject: [PATCH 44/53] Update interactions/client/get.py Co-authored-by: Max --- interactions/client/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 5142e1c3c..7483a7c59 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -159,7 +159,7 @@ def get(*args, **kwargs): .. important:: The ``parent_id`` represents the channel or guild id that belongs to the objects you want to get. It is - called ``parent_id`` bcause ``guild_or_channel_id`` would be horrible to type out every time. + called ``parent_id`` because ``guild_or_channel_id`` would be horrible to type out every time. Getting a list of an object: Here you will see 1 example of how to get a list of objects. The possibilities on how to force (and their From bc9eb45f23ff2614b9ec3ff801ec6bf660b0c911 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 21:06:34 +0200 Subject: [PATCH 45/53] refactor: Optimize `_search_iterable` --- interactions/client/get.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 7483a7c59..82485d9e2 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -356,15 +356,17 @@ def _search_iterable(items: Iterable[_T], **kwargs) -> Optional[_T]: ) _arg = str(list(kwargs)[0]) + kwarg = kwargs.get(_arg) + kwarg_is_function: bool = isfunction(kwarg) __obj = next( ( item for item in items if ( - str(getattr(item, _arg, None)) == str(kwargs.get(_arg)) - if not isfunction(kwargs.get(_arg)) - else kwargs.get(_arg)(item) + str(getattr(item, _arg, None)) == str(kwarg) + if not kwarg_is_function + else kwarg(item) ) ), None, From 77c19714c65801bb0789262c1849e64b645f775b Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 21:25:12 +0200 Subject: [PATCH 46/53] refactor: Optimize `_search_iterable`and use a list of tuples instead of a tuple of tuples --- interactions/client/get.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/interactions/client/get.py b/interactions/client/get.py index 82485d9e2..e5a42befb 100644 --- a/interactions/client/get.py +++ b/interactions/client/get.py @@ -311,16 +311,15 @@ def _get_cache( if _list: _obj = [] if _object == Member: # Can't be more dynamic on this - _values = () _guild_id = Snowflake(kwargs.get("guild_id")) - for _id in kwargs.get("member_ids"): - _values += ( - ( - _guild_id, - Snowflake(_id), - ), + _values = [ + ( + _guild_id, + Snowflake(_id), ) - _obj.extend(client._http.cache[_object].get(item) for item in _values) + for _id in kwargs.get("member_ids") + ] + _obj.extend(client._http.cache[_object].get(item, None) for item in _values) else: _obj.extend( From 44a9ac23e8def1dba179dda4cc6f92d7f5092a09 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 22:59:20 +0200 Subject: [PATCH 47/53] Update migration.rst --- docs/migration.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/migration.rst b/docs/migration.rst index b7d59e386..1f2358d14 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -62,3 +62,13 @@ portal and add the intent to your current intents when connecting: from interactions import Client, Intents bot = Client("TOKEN", intents=Intents.DEFAULT | Intents.GUILD_MESSAGE_CONTENT) + + + +4.1.2 -> 4.3.0 +~~~~~~~~~~~~~~~ + +A new big change in this release is the ``get` utility method. +It allows you to no longer use ``**await bot._http...``. + +You can get more information by reading the :ref:``get-documentation `` From 3ebb45d656ed14f203cbaec7c2420100b7588a14 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:10:56 +0200 Subject: [PATCH 48/53] Update migration.rst --- docs/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index 1f2358d14..3b0838b27 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,4 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:``get-documentation `` +You can get more information by reading the :ref:`get-documentation ` From d46138f22f34bb0f2cb1044a83dd75dc621bbad6 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:13:47 +0200 Subject: [PATCH 49/53] Update migration.rst --- docs/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index 3b0838b27..03c0adf80 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,4 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:`get-documentation ` +You can get more information by reading the :ref:`get-documentation ` From 1c6100323689d82cc676f8f9fbbdb555e89b74b8 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:16:35 +0200 Subject: [PATCH 50/53] Update migration.rst --- docs/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index 03c0adf80..e222113ba 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,4 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:`get-documentation ` +You can get more information by reading the :ref:`get-documentation ` From bc7a779c3a25fd9022fd36d91dc56ce7b05cd856 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:22:25 +0200 Subject: [PATCH 51/53] Update migration.rst --- docs/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index e222113ba..bb84047d1 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,4 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:`get-documentation ` +You can get more information by reading the :ref:`get-documentation ` From 397f590c586b2f8183a6e2006886ee7b7d93c194 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:25:33 +0200 Subject: [PATCH 52/53] Update migration.rst --- docs/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index bb84047d1..e06d74bd8 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,4 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:`get-documentation ` +You can get more information by reading the :ref:`get-documentation ` From 9e0883e20420e89c84e42e82064252376a981e58 Mon Sep 17 00:00:00 2001 From: EdVraz <88881326+EdVraz@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:30:17 +0200 Subject: [PATCH 53/53] Update migration.rst --- docs/migration.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/migration.rst b/docs/migration.rst index e06d74bd8..d34869215 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -71,4 +71,7 @@ portal and add the intent to your current intents when connecting: A new big change in this release is the ``get` utility method. It allows you to no longer use ``**await bot._http...``. -You can get more information by reading the :ref:`get-documentation ` +You can get more information by reading the `get-documentation`_ + + +.. _get-documentation: https://interactionspy.readthedocs.io/en/latest/get.html#the-get-utility-method