diff --git a/interactions/api/models/attrs_utils.py b/interactions/api/models/attrs_utils.py index 86b6d9841..0a0ec69fb 100644 --- a/interactions/api/models/attrs_utils.py +++ b/interactions/api/models/attrs_utils.py @@ -1,3 +1,4 @@ +from copy import deepcopy from functools import wraps from typing import Dict, Mapping, Optional, Tuple @@ -18,9 +19,16 @@ class DictSerializerMixin: _extras: dict = attrs.field(init=False, repr=False) """A dict containing values that were not serialized from Discord.""" + __deepcopy__ = False + """Should the kwargs be deepcopied or not?""" + def __init__(self, kwargs_dict: dict = None, /, **other_kwargs): kwargs = kwargs_dict or other_kwargs client = kwargs.pop("_client", None) + + if self.__deepcopy__: + kwargs = deepcopy(kwargs) + self._json = kwargs.copy() passed_kwargs = {} @@ -169,6 +177,23 @@ def inner_convert_object(value): return inner_convert_object +def deepcopy_kwargs(cls: Optional[type] = None): + """ + A decorator to make the DictSerializerMixin deepcopy the kwargs before processing them. + This can help avoid weird bugs with some objects, though will error out in others. + """ + + def decorator(cls: type): + cls.__deepcopy__ = True # type: ignore + return cls + + if cls is not None: + cls.__deepcopy__ = True # type: ignore + return cls + + return decorator + + define_defaults = dict(kw_only=True, eq=False, init=False, on_setattr=attrs.setters.NO_OP) diff --git a/interactions/api/models/attrs_utils.pyi b/interactions/api/models/attrs_utils.pyi index 5ae1d0b66..86af90940 100644 --- a/interactions/api/models/attrs_utils.pyi +++ b/interactions/api/models/attrs_utils.pyi @@ -18,6 +18,8 @@ class DictSerializerMixin: _json: dict = attrs.field(init=False) _extras: dict = attrs.field(init=False) """A dict containing values that were not serialized from Discord.""" + __deepcopy__: bool = attrs.field(init=False) + """Should the kwargs be deepcopied or not?""" def __init__(self, kwargs_dict: dict = None, /, **other_kwargs): ... @attrs.define(eq=False, init=False, on_setattr=attrs.setters.NO_OP) @@ -63,3 +65,9 @@ def convert_dict( value_converter: Optional[Callable[[Any], _P]] = None, ) -> Callable[[Dict[Any, Any]], Dict[_T, _P]]: """A helper function to convert the keys and values of a dictionary with the specified converters""" + +def deepcopy_kwargs(cls: Optional[Type[_T]] = None) -> Callable[[Any], _T]: + """ + A decorator to make the DictSerializerMixin deepcopy the kwargs before processing them. + This can help avoid weird bugs with some objects, though will error out in others. + """ diff --git a/interactions/api/models/message.py b/interactions/api/models/message.py index f3913943a..f664e6004 100644 --- a/interactions/api/models/message.py +++ b/interactions/api/models/message.py @@ -11,6 +11,7 @@ DictSerializerMixin, convert_list, convert_type, + deepcopy_kwargs, define, field, ) @@ -457,6 +458,7 @@ def __setattr__(self, key, value) -> None: @define() +@deepcopy_kwargs() class Embed(DictSerializerMixin): """ A class object representing an embed. @@ -877,20 +879,6 @@ class Message(ClientSerializerMixin): converter=convert_list(Sticker), default=None ) # deprecated - def __attrs_post_init__(self): - if self.embeds: - # note from astrea49: this dumb fix is necessary to make sure the _json - # is correct when using a dict for the embeds when initializing - # otherwise, attributes like the footer will be missing or incorrect - # i have no idea why this is necessary and i've have tried debugging for hours - # to find why it's necessary, but i've came up with nothing - # by all means, the converters and json should be correct, but it isn't - # this also happens nowhere else as far as i can tell - - # this line should NOT be touched unless you somehow find a solution to - # this, or end up modifying how _json works altogether - self._json["embeds"] = [e._json for e in self.embeds] - async def get_channel(self) -> Channel: """ Gets the channel where the message was sent.