keyboa.button.Button._verified_button_tuple()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 1
dl 0
loc 15
ccs 7
cts 7
cp 1
crap 4
rs 9.95
c 0
b 0
f 0
1
# -*- coding:utf-8 -*-
2 1
"""
3
This module contains all the necessary functions for
4
creating buttons for telegram inline keyboards.
5
"""
6 1
from dataclasses import dataclass
7 1
from typing import Optional
8 1
from telebot.types import InlineKeyboardButton
9 1
from keyboa.button_check import ButtonCheck
10 1
from keyboa.constants import (
11
    InlineButtonData,
12
    CallbackDataMarker,
13
    callback_data_types,
14
    button_text_types,
15
    ButtonText,
16
)
17
18
19 1
@dataclass
20 1
class Button(ButtonCheck):
21
    """Default Button class
22
    :button_data: InlineButtonData - an object from which the button will be created:
23
    • If string or an integer, it will be used for both text and callback.
24
    • If tuple, the zero element [0] will be the text, and the first [1] will be the callback.
25
    • If dictionary, there are two options:
26
        > if there is no "text" key in dictionary and only one key exists,
27
            the key will be the text, and the value will be the callback.
28
            In this case, no verification of the dictionary's contents is performed!
29
        > if "text" key exists, function passes the whole dictionary to InlineKeyboardButton,
30
            where dictionary's keys represent object's parameters
31
            and dictionary's values represent parameters' values accordingly.
32
        In all other cases ValueError will be called.
33
34
    :front_marker: CallbackDataMarker - a string to be added to the left side of callback.
35
        Optional. The default value is None.
36
37
    :back_marker: CallbackDataMarker - a string to be added to the right side of callback.
38
        Optional. The default value is None.
39
40
    :copy_text_to_callback: If enabled and button_data is a string or integer,
41
        function will copy button text to callback data (and add markers if they exist).
42
        Optional. The default value is False."""
43
44 1
    button_data: InlineButtonData = None
45 1
    front_marker: CallbackDataMarker = str()
46 1
    back_marker: CallbackDataMarker = str()
47 1
    copy_text_to_callback: Optional[bool] = None
48
49 1
    def __call__(self, *args, **kwargs):
50 1
        return self.generate()
51
52 1
    def generate(self) -> InlineKeyboardButton:
53
        """
54
        This function creates an InlineKeyboardButton object from various data types,
55
        such as str, int, tuple, dict.
56
        :return: InlineKeyboardButton
57
58
        Covered by tests.
59
        """
60
61 1
        if isinstance(self.button_data, InlineKeyboardButton):
62 1
            return self.button_data
63
64 1
        if isinstance(self.button_data, dict) and self.button_data.get("text"):
65 1
            return InlineKeyboardButton(**self.button_data)
66
67 1
        self.is_auto_copy_text_to_callback()
68
69 1
        button_tuple = self._verified_button_tuple
70 1
        text = self.get_text(button_tuple)
71 1
        raw_callback = self.get_callback(button_tuple)
72 1
        callback_data = self.get_callback_data(
73
            raw_callback, self.front_marker, self.back_marker
74
        )
75
76 1
        prepared_button = {"text": text, "callback_data": callback_data}
77
78 1
        return InlineKeyboardButton(**prepared_button)
79
80 1
    @property
81
    def button(self):
82 1
        """Property that generates and returns an InlineKeyboardButton.
83
        
84 1
        This is a convenience property that calls the generate() method.
85
        It allows accessing the button directly as an attribute.
86
        
87
        Returns:
88
            telebot.types.InlineKeyboardButton: The generated inline keyboard button.
89 1
        """
90
        return self.generate()
91
92 1
    def is_auto_copy_text_to_callback(self):
93
        """
94 1
        Enable copy_text_to_callback parameter if button_data is str or int
95 1
        :return:
96
        """
97
        if self.copy_text_to_callback is None and isinstance(
98
            self.button_data, (str, int)
99
        ):
100 1
            self.copy_text_to_callback = True
101 1
102 1
    @classmethod
103
    def get_callback(cls, button_data: tuple) -> str:
104 1
        """
105 1
        :param button_data:
106
        :return:
107
        """
108
        callback = button_data[1]
109
        cls.is_callback_proper_type(callback)
110
        return callback
111
112
    @classmethod
113
    def get_callback_data(
114
        cls,
115
        raw_callback: CallbackDataMarker,
116
        front_marker: CallbackDataMarker = str(),
117
        back_marker: CallbackDataMarker = str(),
118 1
    ) -> str:
119 1
        """
120
        :param raw_callback:
121 1
        :param front_marker:
122
        :param back_marker:
123 1
        :return:
124 1
        """
125
126 1
        front_marker = cls.get_checked_marker(front_marker)
127
        back_marker = cls.get_checked_marker(back_marker)
128 1
129
        callback_data = f"{front_marker}{raw_callback}{back_marker}"
130 1
131 1
        if not callback_data:
132
            raise ValueError("The callback data cannot be empty.")
133
134
        cls.is_callback_data_in_limits(callback_data)
135
136 1
        return callback_data
137 1
138
    @staticmethod
139 1
    def get_checked_marker(marker: CallbackDataMarker) -> CallbackDataMarker:
140 1
        """
141
        :param marker:
142
        :return:
143
        """
144 1
        if marker is None:
145
            marker = str()
146 1
147
        if not isinstance(marker, callback_data_types):
148 1
            type_error_message = (
149 1
                f"Marker could not have {type(marker)} type. "
150
                f"Only {CallbackDataMarker} allowed."
151
            )
152
            raise TypeError(type_error_message)
153
154 1
        return marker
155 1
156 1
    @staticmethod
157
    def get_text(button_data: tuple) -> str:
158
        """
159
        :param button_data:
160 1
        :return:
161 1
        """
162 1
        raw_text = button_data[0]
163 1
        if not isinstance(raw_text, button_text_types):
164 1
            type_error_message = (
165
                f"Button text cannot be {type(raw_text)}. "
166 1
                f"Only {ButtonText} allowed."
167 1
            )
168
            raise TypeError(type_error_message)
169
        text = str(raw_text)
170
        if not text:
171 1
            raise ValueError("Button text cannot be empty.")
172
        return text
173 1
174
    @property
175 1
    def _verified_button_tuple(self) -> tuple:
176 1
        """
177
        :return:
178
        """
179
        self.is_button_data_proper_type(self.button_data)
180 1
181
        btn_tuple = self._raw_tuple_from_button_data
182 1
183 1
        if len(btn_tuple) == 1 or btn_tuple[1] is None:
184
            btn_tuple = (
185
                btn_tuple[0],
186
                btn_tuple[0] if self.copy_text_to_callback else str(),
187 1
            )
188 1
        return btn_tuple
189
190
    @property
191
    def _raw_tuple_from_button_data(self) -> tuple:
192
        """
193 1
        :return:
194 1
        """
195 1
        if isinstance(self.button_data, (str, int)):
196
            btn_tuple = (
197
                self.button_data,
198
                self.button_data if self.copy_text_to_callback else str(),
199 1
            )
200
201 1
        elif isinstance(self.button_data, dict):
202
            if len(self.button_data.keys()) != 1:
203 1
                value_type_error = (
204 1
                    "Cannot convert dictionary to InlineButtonData object. "
205
                    "You passed more than one item, but did not add 'text' key."
206
                )
207
                raise ValueError(value_type_error)
208
209
            btn_tuple = next(iter(self.button_data.items()))
210
        else:
211
            btn_tuple = self.button_data
212
        return btn_tuple
213