Freeze Classes Without Arguments (#3453)

This commit is contained in:
Bibo-Joshi 2023-01-01 13:04:37 +01:00 committed by GitHub
parent f3a9b74445
commit 7b61a30fb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 118 additions and 85 deletions

View file

@ -122,6 +122,11 @@ class ForumTopicClosed(TelegramObject):
__slots__ = () __slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()
class ForumTopicReopened(TelegramObject): class ForumTopicReopened(TelegramObject):
""" """
@ -132,3 +137,8 @@ class ForumTopicReopened(TelegramObject):
""" """
__slots__ = () __slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()

View file

@ -19,9 +19,15 @@
"""This module contains an object that represents a Telegram CallbackGame.""" """This module contains an object that represents a Telegram CallbackGame."""
from telegram._telegramobject import TelegramObject from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class CallbackGame(TelegramObject): class CallbackGame(TelegramObject):
"""A placeholder, currently holds no information. Use BotFather to set up your game.""" """A placeholder, currently holds no information. Use BotFather to set up your game."""
__slots__ = () __slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()

View file

@ -56,7 +56,7 @@ class InputContactMessageContent(InputMessageContent):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
# Required # Required
self.phone_number = phone_number self.phone_number = phone_number
self.first_name = first_name self.first_name = first_name
@ -65,5 +65,3 @@ class InputContactMessageContent(InputMessageContent):
self.vcard = vcard self.vcard = vcard
self._id_attrs = (self.phone_number,) self._id_attrs = (self.phone_number,)
self._freeze()

View file

@ -200,6 +200,7 @@ class InputInvoiceMessageContent(InputMessageContent):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
# Required # Required
self.title = title self.title = title
self.description = description self.description = description
@ -232,8 +233,6 @@ class InputInvoiceMessageContent(InputMessageContent):
self.prices, self.prices,
) )
self._freeze()
@classmethod @classmethod
def de_json( def de_json(
cls, data: Optional[JSONDict], bot: "Bot" cls, data: Optional[JSONDict], bot: "Bot"

View file

@ -83,6 +83,7 @@ class InputLocationMessageContent(InputMessageContent):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
# Required # Required
self.latitude = latitude self.latitude = latitude
self.longitude = longitude self.longitude = longitude
@ -97,8 +98,6 @@ class InputLocationMessageContent(InputMessageContent):
self._id_attrs = (self.latitude, self.longitude) self._id_attrs = (self.latitude, self.longitude)
self._freeze()
HORIZONTAL_ACCURACY: ClassVar[int] = constants.LocationLimit.HORIZONTAL_ACCURACY HORIZONTAL_ACCURACY: ClassVar[int] = constants.LocationLimit.HORIZONTAL_ACCURACY
""":const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY` """:const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY`

View file

@ -19,6 +19,7 @@
"""This module contains the classes that represent Telegram InputMessageContent.""" """This module contains the classes that represent Telegram InputMessageContent."""
from telegram._telegramobject import TelegramObject from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict
class InputMessageContent(TelegramObject): class InputMessageContent(TelegramObject):
@ -32,3 +33,8 @@ class InputMessageContent(TelegramObject):
""" """
__slots__ = () __slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()

View file

@ -78,6 +78,7 @@ class InputTextMessageContent(InputMessageContent):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
# Required # Required
self.message_text = message_text self.message_text = message_text
# Optionals # Optionals
@ -86,5 +87,3 @@ class InputTextMessageContent(InputMessageContent):
self.disable_web_page_preview = disable_web_page_preview self.disable_web_page_preview = disable_web_page_preview
self._id_attrs = (self.message_text,) self._id_attrs = (self.message_text,)
self._freeze()

View file

@ -84,7 +84,7 @@ class InputVenueMessageContent(InputMessageContent):
api_kwargs: JSONDict = None, api_kwargs: JSONDict = None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
with self._unfrozen():
# Required # Required
self.latitude = latitude self.latitude = latitude
self.longitude = longitude self.longitude = longitude
@ -101,5 +101,3 @@ class InputVenueMessageContent(InputMessageContent):
self.longitude, self.longitude,
self.title, self.title,
) )
self._freeze()

View file

@ -92,6 +92,10 @@ class TelegramObject:
__INIT_PARAMS_CHECK: Optional[Type["TelegramObject"]] = None __INIT_PARAMS_CHECK: Optional[Type["TelegramObject"]] = None
def __init__(self, *, api_kwargs: JSONDict = None) -> None: def __init__(self, *, api_kwargs: JSONDict = None) -> None:
# Setting _frozen to `False` here means that classes without arguments still need to
# implement __init__. However, with `True` would mean increased usage of
# `with self._unfrozen()` in the `__init__` of subclasses and we have fewer empty
# classes than classes with arguments.
self._frozen: bool = False self._frozen: bool = False
self._id_attrs: Tuple[object, ...] = () self._id_attrs: Tuple[object, ...] = ()
self._bot: Optional["Bot"] = None self._bot: Optional["Bot"] = None

View file

@ -42,6 +42,11 @@ class VideoChatStarted(TelegramObject):
__slots__ = () __slots__ = ()
def __init__(self, *, api_kwargs: JSONDict = None) -> None:
super().__init__(api_kwargs=api_kwargs)
self._freeze()
class VideoChatEnded(TelegramObject): class VideoChatEnded(TelegramObject):
""" """

View file

@ -185,6 +185,7 @@ class InputMessageContentDWPP(InputMessageContent):
api_kwargs=None, api_kwargs=None,
): ):
super().__init__(api_kwargs=api_kwargs) super().__init__(api_kwargs=api_kwargs)
self._unfreeze()
self.message_text = message_text self.message_text = message_text
self.disable_web_page_preview = disable_web_page_preview self.disable_web_page_preview = disable_web_page_preview

View file

@ -428,7 +428,8 @@ class TestTelegramObject:
@pytest.mark.parametrize("cls", TO_SUBCLASSES, ids=[cls.__name__ for cls in TO_SUBCLASSES]) @pytest.mark.parametrize("cls", TO_SUBCLASSES, ids=[cls.__name__ for cls in TO_SUBCLASSES])
def test_subclasses_are_frozen(self, cls): def test_subclasses_are_frozen(self, cls):
if cls.__name__.startswith("_"): if cls is TelegramObject or cls.__name__.startswith("_"):
# Protected classes don't need to be frozen and neither does the base class
return return
# instantiating each subclass would be tedious as some attributes require special init # instantiating each subclass would be tedious as some attributes require special init
@ -437,10 +438,17 @@ class TestTelegramObject:
source_file = inspect.getsourcefile(cls.__init__) source_file = inspect.getsourcefile(cls.__init__)
parents = Path(source_file).parents parents = Path(source_file).parents
is_test_file = Path(__file__).parent.resolve() in parents is_test_file = Path(__file__).parent.resolve() in parents
if is_test_file or source_file.endswith("telegramobject.py"):
# classes without their own `__init__` can be ignored if is_test_file:
# If the class is defined in a test file, we don't want to test it.
return return
if source_file.endswith("telegramobject.py"):
pytest.fail(
f"{cls.__name__} does not have its own `__init__` "
"and can therefore not be frozen correctly"
)
source_lines, first_line = inspect.getsourcelines(cls.__init__) source_lines, first_line = inspect.getsourcelines(cls.__init__)
# We use regex matching since a simple "if self._freeze() in source_lines[-1]" would also # We use regex matching since a simple "if self._freeze() in source_lines[-1]" would also