xloft 0.5.1__py3-none-any.whl → 0.9.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xloft might be problematic. Click here for more details.

xloft/__init__.py CHANGED
@@ -1,17 +1,35 @@
1
- """(XLOFT) X-Library of tools.
2
-
3
- Modules exported by this package:
4
-
5
- - `namedtuple`- Class imitates the behavior of the _named tuple_.
6
- - `human` - A collection of instruments for converting data to format is convenient for humans.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- __all__ = (
12
- "to_human_size",
13
- "NamedTuple",
14
- )
15
-
16
- from xloft.human import to_human_size
17
- from xloft.namedtuple import NamedTuple
1
+ #  ____  ____  _____       ___   ________  _________
2
+ # |_  _||_  _||_   _|    .'   `.|_   __  ||  _   _  |
3
+ #   \ \  / /    | |     /  .-.  \ | |_ \_||_/ | | \_|
4
+ #    > `' <     | |   _ | |   | | |  _|       | |
5
+ #  _/ /'`\ \_  _| |__/ |\  `-'  /_| |_       _| |_
6
+ # |____||____||________| `.___.'|_____|     |_____|
7
+ #
8
+ #
9
+ # Copyright (c) 2025 Gennady Kostyunin
10
+ # XLOFT is free software under terms of the MIT License.
11
+ # Repository https://github.com/kebasyaty/xloft
12
+ #
13
+ """(XLOFT) X-Library of tools.
14
+
15
+ Modules exported by this package:
16
+
17
+ - `namedtuple`- Class imitates the behavior of the _named tuple_.
18
+ - `converters` - Collection of tools for converting data.
19
+ - `itis` - Tools for determining something.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ __all__ = (
25
+ "int_to_roman",
26
+ "roman_to_int",
27
+ "to_human_size",
28
+ "is_number",
29
+ "is_palindrome",
30
+ "NamedTuple",
31
+ )
32
+
33
+ from xloft.converters import int_to_roman, roman_to_int, to_human_size
34
+ from xloft.itis import is_number, is_palindrome
35
+ from xloft.namedtuple import NamedTuple
@@ -0,0 +1,19 @@
1
+ """Collection of tools for converting data.
2
+
3
+ The module contains the following functions:
4
+
5
+ - `to_human_size(n_bytes)` - Returns a humanized string: 200 bytes | 1 KB | 1.5 MB etc.
6
+ - `int_to_roman` - Convert an integer to Roman.
7
+ - `roman_to_int` - Convert to integer from Roman.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ __all__ = (
13
+ "to_human_size",
14
+ "int_to_roman",
15
+ "roman_to_int",
16
+ )
17
+
18
+ from xloft.converters.human_size import to_human_size
19
+ from xloft.converters.roman import int_to_roman, roman_to_int
@@ -0,0 +1,39 @@
1
+ """Converts the number of bytes into a human-readable format.
2
+
3
+ The module contains the following functions:
4
+
5
+ - `to_human_size(n_bytes)` - Returns a humanized string: 200 bytes | 1 KB | 1.5 MB etc.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ __all__ = ("to_human_size",)
11
+
12
+ import math
13
+
14
+
15
+ def to_human_size(n_bytes: int) -> str:
16
+ """Converts the number of bytes into a human-readable format.
17
+
18
+ Examples:
19
+ >>> from xloft import to_human_size
20
+ >>> to_human_size(200)
21
+ 200 bytes
22
+ >>> to_human_size(1048576)
23
+ 1 MB
24
+ >>> to_human_size(1048575)
25
+ 1023.999 KB
26
+
27
+ Args:
28
+ n_bytes: The number of bytes.
29
+
30
+ Returns:
31
+ Returns a humanized string: 200 bytes | 1 KB | 1.5 MB etc.
32
+ """
33
+ idx: int = math.floor(math.log(n_bytes) / math.log(1024))
34
+ ndigits: int = [0, 3, 6, 9, 12][idx]
35
+ human_size: int | float = n_bytes if n_bytes < 1024 else abs(round(n_bytes / pow(1024, idx), ndigits))
36
+ order = ["bytes", "KB", "MB", "GB", "TB"][idx]
37
+ if math.modf(human_size)[0] == 0.0:
38
+ human_size = int(human_size)
39
+ return f"{human_size} {order}"
@@ -0,0 +1,116 @@
1
+ """Convert an integer to Roman and vice versa.
2
+
3
+ The module contains the following functions:
4
+
5
+ - `int_to_roman` - Convert an integer to Roman.
6
+ - `roman_to_int` - Convert to integer from Roman.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ __all__ = (
12
+ "int_to_roman",
13
+ "roman_to_int",
14
+ )
15
+
16
+ ROMAN = [
17
+ (1000, "M"),
18
+ (900, "CM"),
19
+ (500, "D"),
20
+ (400, "CD"),
21
+ (100, "C"),
22
+ (90, "XC"),
23
+ (50, "L"),
24
+ (40, "XL"),
25
+ (10, "X"),
26
+ (9, "IX"),
27
+ (5, "V"),
28
+ (4, "IV"),
29
+ (1, "I"),
30
+ ]
31
+
32
+
33
+ def int_to_roman(number: int) -> str:
34
+ """Convert an integer to Roman.
35
+
36
+ Examples:
37
+ >>> from xloft import int_to_roman
38
+ >>> int_to_roman(1994)
39
+ MCMXCIV
40
+
41
+ Args:
42
+ number (int): Integer.
43
+
44
+ Returns:
45
+ Roman numeral string.
46
+ """
47
+ result = ""
48
+ for arabic, roman in ROMAN:
49
+ (factor, number) = divmod(number, arabic)
50
+ result += roman * factor
51
+ return result
52
+
53
+
54
+ def roman_to_int(roman: str) -> int:
55
+ """Convert to integer from Roman.
56
+
57
+ Examples:
58
+ >>> from xloft import roman_to_int
59
+ >>> roman_to_int("MCMXCIV")
60
+ 1994
61
+
62
+ Args:
63
+ roman (str): Roman numeral string.
64
+
65
+ Returns:
66
+ Integer.
67
+ """
68
+ i_count = roman.count("I")
69
+ v_count = roman.count("V")
70
+ x_count = roman.count("X")
71
+ l_count = roman.count("L")
72
+ c_count = roman.count("C")
73
+ d_count = roman.count("D")
74
+ m_count = roman.count("M")
75
+
76
+ iv_count = roman.count("IV")
77
+ i_count -= iv_count
78
+ v_count -= iv_count
79
+
80
+ ix_count = roman.count("IX")
81
+ i_count -= ix_count
82
+ x_count -= ix_count
83
+
84
+ xl_count = roman.count("XL")
85
+ x_count -= xl_count
86
+ l_count -= xl_count
87
+
88
+ xc_count = roman.count("XC")
89
+ x_count -= xc_count
90
+ c_count -= xc_count
91
+
92
+ cd_count = roman.count("CD")
93
+ c_count -= cd_count
94
+ d_count -= cd_count
95
+
96
+ cm_count = roman.count("CM")
97
+ c_count -= cm_count
98
+ m_count -= cm_count
99
+
100
+ total = 0
101
+ total += 1 * i_count
102
+ total += 5 * v_count
103
+ total += 10 * x_count
104
+ total += 50 * l_count
105
+ total += 100 * c_count
106
+ total += 500 * d_count
107
+ total += 1000 * m_count
108
+
109
+ total += 4 * iv_count
110
+ total += 9 * ix_count
111
+ total += 40 * xl_count
112
+ total += 90 * xc_count
113
+ total += 400 * cd_count
114
+ total += 900 * cm_count
115
+
116
+ return total
xloft/errors.py CHANGED
@@ -1,26 +1,32 @@
1
- """XLOT Exceptions."""
2
-
3
- from __future__ import annotations
4
-
5
-
6
- class XLOTException(Exception):
7
- """Root Custom Exception."""
8
-
9
- def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]# noqa: D107
10
- super().__init__(*args, **kwargs)
11
-
12
-
13
- class AttributeDoesNotSetValue(XLOTException):
14
- """Exception is raised if the attribute does not setting value."""
15
-
16
- def __init__(self, attribute_name: str) -> None: # noqa: D107
17
- self.message = f"The attribute `{attribute_name}` does not setting value!"
18
- super().__init__(self.message)
19
-
20
-
21
- class AttributeCannotBeDelete(XLOTException):
22
- """Exception is raised if the attribute cannot be delete."""
23
-
24
- def __init__(self, attribute_name: str) -> None: # noqa: D107
25
- self.message = f"The attribute `{attribute_name}` cannot be delete!"
26
- super().__init__(self.message)
1
+ """XLOT Exceptions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__ = (
6
+ "XLOTException",
7
+ "AttributeDoesNotSetValueError",
8
+ "AttributeCannotBeDeleteError",
9
+ )
10
+
11
+
12
+ class XLOTException(Exception):
13
+ """Root Custom Exception."""
14
+
15
+ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def]# noqa: D107
16
+ super().__init__(*args, **kwargs)
17
+
18
+
19
+ class AttributeDoesNotSetValueError(XLOTException):
20
+ """Exception is raised if the attribute does not setting value."""
21
+
22
+ def __init__(self, attribute_name: str) -> None: # noqa: D107
23
+ self.message = f"The attribute `{attribute_name}` does not setting value!"
24
+ super().__init__(self.message)
25
+
26
+
27
+ class AttributeCannotBeDeleteError(XLOTException):
28
+ """Exception is raised if the attribute cannot be delete."""
29
+
30
+ def __init__(self, attribute_name: str) -> None: # noqa: D107
31
+ self.message = f"The attribute `{attribute_name}` cannot be delete!"
32
+ super().__init__(self.message)
xloft/itis.py ADDED
@@ -0,0 +1,60 @@
1
+ """Tools for determining something.
2
+
3
+ The module contains the following functions:
4
+
5
+ - `is_number` - Check if a string is a number.
6
+ - `is_palindrome` - Check if a string is a palindrome.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ __all__ = (
12
+ "is_number",
13
+ "is_palindrome",
14
+ )
15
+
16
+
17
+ def is_number(value: str) -> bool:
18
+ """Check if a string is a number.
19
+
20
+ Only decimal numbers.
21
+
22
+ Examples:
23
+ >>> from xloft import is_number
24
+ >>> is_number("123")
25
+ True
26
+
27
+ Args:
28
+ value (str): Some kind of string.
29
+
30
+ Returns:
31
+ True, if the string is a number.
32
+ """
33
+ try:
34
+ float(value)
35
+ return True
36
+ except ValueError:
37
+ return False
38
+
39
+
40
+ def is_palindrome(value: str) -> bool:
41
+ """Check if a string is a palindrome.
42
+
43
+ Examples:
44
+ >>> from xloft import is_palindrome
45
+ >>> is_palindrome("Go hang a salami, I'm a lasagna hog")
46
+ True
47
+
48
+ Args:
49
+ value (str): Alpha-numeric string.
50
+
51
+ Returns:
52
+ Boolean value.
53
+ """
54
+ if not isinstance(value, str):
55
+ raise TypeError("The value is not a string!")
56
+ if not len(value):
57
+ raise ValueError("The string must not be empty!")
58
+ string_list = [char.lower() for char in value if char.isalnum()]
59
+ reverse_list = string_list[::-1]
60
+ return reverse_list == string_list
xloft/namedtuple.py CHANGED
@@ -1,257 +1,206 @@
1
- """This module contains the implementation of the `NamedTuple` class.
2
-
3
- `NamedTuple` class imitates the behavior of the _named tuple_.
4
-
5
- Examples:
6
- >>> from xloft import NamedTuple
7
- >>> nt = NamedTuple(x=10, y="Hello", _id="507c7f79bcf86cd7994f6c0e")
8
- >>> nt.x
9
- 10
10
- >>> nt.y
11
- Hello
12
- >>> nt._id
13
- 507c7f79bcf86cd7994f6c0e
14
- >>> nt.z
15
- KeyError
16
- >>> len(nt)
17
- 3
18
- >>> nt.keys()
19
- ["x", "y", "_id"]
20
- >>> nt.values()
21
- [10, "Hello", "507c7f79bcf86cd7994f6c0e"]
22
- >>> nt.has_key("x")
23
- True
24
- >>> nt.has_key("z")
25
- False
26
- >>> nt.has_value(10)
27
- True
28
- >>> nt.has_value([1, 2, 3])
29
- False
30
- >>> nt.get("x")
31
- 10
32
- >>> nt.get("z")
33
- None
34
- >>> d = nt.to_dict()
35
- >>> d["x"]
36
- 10
37
- >>> for key, val in nt.items():
38
- ... print(f"Key: {key}, Value: {val}")
39
- "Key: x, Value: 10"
40
- "Key: y, Value: Hello"
41
- "Key: _id, value: 507c7f79bcf86cd7994f6c0e"
42
- >>> nt.update("x", 20)
43
- >>> nt.x
44
- 20
45
- >>> nt.update("z", [1, 2, 3])
46
- KeyError
47
- >>> nt["z"] = [1, 2, 3]
48
- TypeError
49
- >>> nt.x = 20
50
- Error: AttributeDoesNotSetValue
51
- >>> del nt.x
52
- Error: AttributeCannotBeDelete
53
- """
54
-
55
- from __future__ import annotations
56
-
57
- from typing import Any
58
-
59
- from xloft.errors import (
60
- AttributeCannotBeDelete,
61
- AttributeDoesNotSetValue,
62
- )
63
-
64
-
65
- class NamedTuple:
66
- """This class imitates the behavior of the _named tuple_."""
67
-
68
- def __init__(self, **kwargs: dict[str, Any]) -> None: # noqa: D107
69
- self.__dict__["_jWjSaNy1RbtQinsN_keys"] = []
70
- for name, value in kwargs.items():
71
- self.__dict__[name] = value
72
- self._jWjSaNy1RbtQinsN_keys.append(name)
73
-
74
- def __len__(self) -> int:
75
- """Get the number of elements.
76
-
77
- Examples:
78
- >>> from xloft import NamedTuple
79
- >>> nt = NamedTuple(x=10, y="Hello")
80
- >>> len(nt)
81
- 2
82
-
83
- Returns:
84
- The number of elements in the tuple.
85
- """
86
- return len(self._jWjSaNy1RbtQinsN_keys)
87
-
88
- def __getattr__(self, name: str) -> Any:
89
- """Getter.
90
-
91
- Examples:
92
- >>> from xloft import NamedTuple
93
- >>> nt = NamedTuple(x=10, y="Hello")
94
- >>> nt.x
95
- 10
96
-
97
- Args:
98
- name: Key name.
99
-
100
- Returns:
101
- Value of key.
102
- """
103
- return self.__dict__[name]
104
-
105
- def __setattr__(self, name: str, value: Any) -> None:
106
- """Blocked Setter."""
107
- raise AttributeDoesNotSetValue(name)
108
-
109
- def __delattr__(self, name: str) -> None:
110
- """Blocked Deleter."""
111
- raise AttributeCannotBeDelete(name)
112
-
113
- def get(self, key: str) -> Any:
114
- """Return the value for key if key is in the dictionary, else `None`.
115
-
116
- Args:
117
- key: Key name.
118
-
119
- Examples:
120
- >>> from xloft import NamedTuple
121
- >>> nt = NamedTuple(x=10, y="Hello")
122
- >>> nt.get("x")
123
- 10
124
-
125
- Returns:
126
- Value of key.
127
- """
128
- value = self.__dict__.get(key)
129
- if value is not None:
130
- return value
131
- return None
132
-
133
- def update(self, key: str, value: Any) -> None:
134
- """Update a value of key.
135
-
136
- Attention: This is an uncharacteristic action for the type `tuple`.
137
-
138
- Args:
139
- key: Key name.
140
- value: Value of key.
141
-
142
- Examples:
143
- >>> from xloft import NamedTuple
144
- >>> nt = NamedTuple(x=10, y="Hello")
145
- >>> nt.update("x", 20)
146
- >>> nt.x
147
- 20
148
-
149
- Returns:
150
- None
151
- """
152
- keys: list[str] = self._jWjSaNy1RbtQinsN_keys
153
- if key not in keys:
154
- err_msg = f"The key `{key}` is missing!"
155
- raise KeyError(err_msg)
156
- self.__dict__[key] = value
157
-
158
- def to_dict(self) -> dict[str, Any]:
159
- """Convert to the dictionary.
160
-
161
- Examples:
162
- >>> from xloft import NamedTuple
163
- >>> nt = NamedTuple(x=10, y="Hello")
164
- >>> d = nt.to_dict()
165
- >>> d["x"]
166
- 10
167
-
168
- Returns:
169
- Dictionary with keys and values of the tuple.
170
- """
171
- attrs: dict[str, Any] = self.__dict__
172
- keys: list[str] = self._jWjSaNy1RbtQinsN_keys
173
- return {key: attrs[key] for key in keys}
174
-
175
- def items(self) -> list[tuple[str, Any]]:
176
- """Return a set-like object providing a view on the NamedTuple's items.
177
-
178
- Examples:
179
- >>> from xloft import NamedTuple
180
- >>> nt = NamedTuple(x=10, y="Hello")
181
- >>> for key, val in nt.items():
182
- ... print(f"Key: {key}, Value: {val}")
183
- "Key: x, Value: 10"
184
- "Key: y, Value: Hello"
185
-
186
- Returns:
187
- list[tuple[str, Any]]
188
- """
189
- attrs: dict[str, Any] = self.__dict__
190
- keys: list[str] = self._jWjSaNy1RbtQinsN_keys
191
- return [(key, attrs[key]) for key in keys]
192
-
193
- def keys(self) -> list[str]:
194
- """Get a list of keys.
195
-
196
- Examples:
197
- >>> from xloft import NamedTuple
198
- >>> nt = NamedTuple(x=10, y="Hello")
199
- >>> nt.keys()
200
- ["x", "y"]
201
-
202
- Returns:
203
- List of keys.
204
- """
205
- return self._jWjSaNy1RbtQinsN_keys
206
-
207
- def values(self) -> list[Any]:
208
- """Get a list of values.
209
-
210
- Examples:
211
- >>> from xloft import NamedTuple
212
- >>> nt = NamedTuple(x=10, y="Hello")
213
- >>> nt.values()
214
- [10, "Hello"]
215
-
216
- Returns:
217
- List of values.
218
- """
219
- attrs: dict[str, Any] = self.__dict__
220
- keys: list[str] = self._jWjSaNy1RbtQinsN_keys
221
- return [attrs[key] for key in keys]
222
-
223
- def has_key(self, key: str) -> bool:
224
- """Returns True if the key exists, otherwise False.
225
-
226
- Args:
227
- key: Key name.
228
-
229
- Examples:
230
- >>> from xloft import NamedTuple
231
- >>> nt = NamedTuple(x=10, y="Hello")
232
- >>> nt.has_key("x")
233
- True
234
-
235
- Returns:
236
- True if the key exists, otherwise False.
237
- """
238
- keys: list[str] = self._jWjSaNy1RbtQinsN_keys
239
- return key in keys
240
-
241
- def has_value(self, value: Any) -> bool:
242
- """Returns True if the value exists, otherwise False.
243
-
244
- Args:
245
- value: Value of key.
246
-
247
- Examples:
248
- >>> from xloft import NamedTuple
249
- >>> nt = NamedTuple(x=10, y="Hello")
250
- >>> nt.has_value(10)
251
- True
252
-
253
- Returns:
254
- True if the value exists, otherwise False.
255
- """
256
- values = self.values()
257
- return value in values
1
+ """This module contains the implementation of the `NamedTuple` class.
2
+
3
+ `NamedTuple` class imitates the behavior of the *named tuple*.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any
9
+
10
+ from xloft.errors import (
11
+ AttributeCannotBeDeleteError,
12
+ AttributeDoesNotSetValueError,
13
+ )
14
+
15
+
16
+ class NamedTuple:
17
+ """This class imitates the behavior of the `named tuple`."""
18
+
19
+ def __init__(self, **kwargs: dict[str, Any]) -> None: # noqa: D107
20
+ self.__dict__["_0D5rSmH9Sy2XUWb5_keys"] = []
21
+ for name, value in kwargs.items():
22
+ self.__dict__[name] = value
23
+ self._0D5rSmH9Sy2XUWb5_keys.append(name)
24
+
25
+ def __len__(self) -> int:
26
+ """Get the number of elements.
27
+
28
+ Examples:
29
+ >>> from xloft import NamedTuple
30
+ >>> nt = NamedTuple(x=10, y="Hello")
31
+ >>> len(nt)
32
+ 2
33
+
34
+ Returns:
35
+ The number of elements in the tuple.
36
+ """
37
+ return len(self._0D5rSmH9Sy2XUWb5_keys)
38
+
39
+ def __getattr__(self, name: str) -> Any:
40
+ """Getter.
41
+
42
+ Examples:
43
+ >>> from xloft import NamedTuple
44
+ >>> nt = NamedTuple(x=10, y="Hello")
45
+ >>> nt.x
46
+ 10
47
+
48
+ Args:
49
+ name: Key name.
50
+
51
+ Returns:
52
+ Value of key.
53
+ """
54
+ return self.__dict__[name]
55
+
56
+ def __setattr__(self, name: str, value: Any) -> None:
57
+ """Blocked Setter."""
58
+ raise AttributeDoesNotSetValueError(name)
59
+
60
+ def __delattr__(self, name: str) -> None:
61
+ """Blocked Deleter."""
62
+ raise AttributeCannotBeDeleteError(name)
63
+
64
+ def get(self, key: str) -> Any:
65
+ """Return the value for key if key is in the dictionary, else `None`.
66
+
67
+ Args:
68
+ key: Key name.
69
+
70
+ Examples:
71
+ >>> from xloft import NamedTuple
72
+ >>> nt = NamedTuple(x=10, y="Hello")
73
+ >>> nt.get("x")
74
+ 10
75
+
76
+ Returns:
77
+ Value of key.
78
+ """
79
+ value = self.__dict__.get(key)
80
+ if value is not None:
81
+ return value
82
+ return None
83
+
84
+ def update(self, key: str, value: Any) -> None:
85
+ """Update a value of key.
86
+
87
+ Attention: This is an uncharacteristic action for the type `tuple`.
88
+
89
+ Args:
90
+ key: Key name.
91
+ value: Value of key.
92
+
93
+ Examples:
94
+ >>> from xloft import NamedTuple
95
+ >>> nt = NamedTuple(x=10, y="Hello")
96
+ >>> nt.update("x", 20)
97
+ >>> nt.x
98
+ 20
99
+
100
+ Returns:
101
+ None
102
+ """
103
+ keys: list[str] = self._0D5rSmH9Sy2XUWb5_keys
104
+ if key not in keys:
105
+ err_msg = f"The key `{key}` is missing!"
106
+ raise KeyError(err_msg)
107
+ self.__dict__[key] = value
108
+
109
+ def to_dict(self) -> dict[str, Any]:
110
+ """Convert to the dictionary.
111
+
112
+ Examples:
113
+ >>> from xloft import NamedTuple
114
+ >>> nt = NamedTuple(x=10, y="Hello")
115
+ >>> d = nt.to_dict()
116
+ >>> d["x"]
117
+ 10
118
+
119
+ Returns:
120
+ Dictionary with keys and values of the tuple.
121
+ """
122
+ attrs: dict[str, Any] = self.__dict__
123
+ keys: list[str] = self._0D5rSmH9Sy2XUWb5_keys
124
+ return {key: attrs[key] for key in keys}
125
+
126
+ def items(self) -> list[tuple[str, Any]]:
127
+ """Return a set-like object providing a view on the NamedTuple's items.
128
+
129
+ Examples:
130
+ >>> from xloft import NamedTuple
131
+ >>> nt = NamedTuple(x=10, y="Hello")
132
+ >>> for key, val in nt.items():
133
+ ... print(f"Key: {key}, Value: {val}")
134
+ "Key: x, Value: 10"
135
+ "Key: y, Value: Hello"
136
+
137
+ Returns:
138
+ list[tuple[str, Any]]
139
+ """
140
+ attrs: dict[str, Any] = self.__dict__
141
+ keys: list[str] = self._0D5rSmH9Sy2XUWb5_keys
142
+ return [(key, attrs[key]) for key in keys]
143
+
144
+ def keys(self) -> list[str]:
145
+ """Get a list of keys.
146
+
147
+ Examples:
148
+ >>> from xloft import NamedTuple
149
+ >>> nt = NamedTuple(x=10, y="Hello")
150
+ >>> nt.keys()
151
+ ["x", "y"]
152
+
153
+ Returns:
154
+ List of keys.
155
+ """
156
+ return self._0D5rSmH9Sy2XUWb5_keys.copy()
157
+
158
+ def values(self) -> list[Any]:
159
+ """Get a list of values.
160
+
161
+ Examples:
162
+ >>> from xloft import NamedTuple
163
+ >>> nt = NamedTuple(x=10, y="Hello")
164
+ >>> nt.values()
165
+ [10, "Hello"]
166
+
167
+ Returns:
168
+ List of values.
169
+ """
170
+ attrs: dict[str, Any] = self.__dict__
171
+ keys: list[str] = self._0D5rSmH9Sy2XUWb5_keys
172
+ return [attrs[key] for key in keys]
173
+
174
+ def has_key(self, key: str) -> bool:
175
+ """Returns True if the key exists, otherwise False.
176
+
177
+ Args:
178
+ key: Key name.
179
+
180
+ Examples:
181
+ >>> from xloft import NamedTuple
182
+ >>> nt = NamedTuple(x=10, y="Hello")
183
+ >>> nt.has_key("x")
184
+ True
185
+
186
+ Returns:
187
+ True if the key exists, otherwise False.
188
+ """
189
+ return key in self._0D5rSmH9Sy2XUWb5_keys
190
+
191
+ def has_value(self, value: Any) -> bool:
192
+ """Returns True if the value exists, otherwise False.
193
+
194
+ Args:
195
+ value: Value of key.
196
+
197
+ Examples:
198
+ >>> from xloft import NamedTuple
199
+ >>> nt = NamedTuple(x=10, y="Hello")
200
+ >>> nt.has_value(10)
201
+ True
202
+
203
+ Returns:
204
+ True if the value exists, otherwise False.
205
+ """
206
+ return value in self.values()
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xloft
3
- Version: 0.5.1
3
+ Version: 0.9.6
4
4
  Summary: (XLOFT) X-Library of tools
5
- Project-URL: Homepage, https://github.com/kebasyaty/xloft
5
+ Project-URL: Homepage, https://kebasyaty.github.io/xloft/
6
6
  Project-URL: Repository, https://github.com/kebasyaty/xloft
7
7
  Project-URL: Source, https://github.com/kebasyaty/xloft
8
8
  Project-URL: Bug Tracker, https://github.com/kebasyaty/xloft/issues
@@ -17,10 +17,12 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Operating System :: MacOS :: MacOS X
18
18
  Classifier: Operating System :: Microsoft :: Windows
19
19
  Classifier: Operating System :: POSIX
20
+ Classifier: Operating System :: POSIX :: Linux
20
21
  Classifier: Programming Language :: Python :: 3
21
22
  Classifier: Programming Language :: Python :: 3 :: Only
22
23
  Classifier: Programming Language :: Python :: 3.12
23
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
24
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
27
  Classifier: Topic :: Software Development :: Libraries
26
28
  Classifier: Topic :: Utilities
@@ -47,21 +49,14 @@ Description-Content-Type: text/markdown
47
49
  <a href="https://pypi.python.org/pypi/xloft/" alt="PyPI status"><img src="https://img.shields.io/pypi/status/xloft.svg" alt="PyPI status"></a>
48
50
  <a href="https://pypi.python.org/pypi/xloft/" alt="PyPI version fury.io"><img src="https://badge.fury.io/py/xloft.svg" alt="PyPI version fury.io"></a>
49
51
  <br>
50
- <a href="https://github.com/kebasyaty/xloft/issues"><img src="https://img.shields.io/github/issues/kebasyaty/xloft.svg" alt="GitHub issues"></a>
51
- <a href="https://pepy.tech/projects/xloft"><img src="https://static.pepy.tech/badge/xloft" alt="PyPI Downloads"></a>
52
- <a href="https://github.com/kebasyaty/xloft/blob/main/LICENSE" alt="GitHub license"><img src="https://img.shields.io/github/license/kebasyaty/xloft" alt="GitHub license"></a>
53
- <a href="https://mypy-lang.org/" alt="Types: Mypy"><img src="https://img.shields.io/badge/types-Mypy-202235.svg?color=0c7ebf" alt="Types: Mypy"></a>
52
+ <a href="https://pyrefly.org/" alt="Types: Pyrefly"><img src="https://img.shields.io/badge/types-Pyrefly-FFB74D.svg" alt="Types: Pyrefly"></a>
54
53
  <a href="https://docs.astral.sh/ruff/" alt="Code style: Ruff"><img src="https://img.shields.io/badge/code%20style-Ruff-FDD835.svg" alt="Code style: Ruff"></a>
55
- <a href="https://github.com/kebasyaty/xloft" alt="PyPI implementation"><img src="https://img.shields.io/pypi/implementation/xloft" alt="PyPI implementation"></a>
56
- <br>
57
54
  <a href="https://pypi.org/project/xloft"><img src="https://img.shields.io/pypi/format/xloft" alt="Format"></a>
58
- <a href="https://github.com/kebasyaty/xloft"><img src="https://img.shields.io/github/languages/top/kebasyaty/xloft" alt="Top"></a>
59
- <a href="https://github.com/kebasyaty/xloft"><img src="https://img.shields.io/github/repo-size/kebasyaty/xloft" alt="Size"></a>
60
- <a href="https://github.com/kebasyaty/xloft"><img src="https://img.shields.io/github/last-commit/kebasyaty/xloft/main" alt="Last commit"></a>
61
- <a href="https://github.com/kebasyaty/xloft/releases/" alt="GitHub release"><img src="https://img.shields.io/github/release/kebasyaty/xloft" alt="GitHub release"></a>
55
+ <a href="https://pepy.tech/projects/xloft"><img src="https://static.pepy.tech/badge/xloft" alt="PyPI Downloads"></a>
56
+ <a href="https://github.com/kebasyaty/xloft/blob/main/LICENSE" alt="GitHub license"><img src="https://img.shields.io/github/license/kebasyaty/xloft" alt="GitHub license"></a>
62
57
  </p>
63
58
  <p align="center">
64
- The collection is represented by three modules of `NamedTuple`, `Human`.
59
+ The collection is represented by three modules of `NamedTuple`, `Converters`, `ItIs`.
65
60
  <br>
66
61
  In the future, new tools can be added.
67
62
  </p>
@@ -70,13 +65,6 @@ Description-Content-Type: text/markdown
70
65
 
71
66
  ##
72
67
 
73
- <img src="https://raw.githubusercontent.com/kebasyaty/xloft/v0/assets/attention.svg" alt="Attention">
74
- <p>
75
- The `quantum` module was deleted.
76
- <br>
77
- <a href="https://pypi.python.org/pypi/quantum-loop/" alt="quantum-loop">He is now here</a>
78
- </p>
79
-
80
68
  <br>
81
69
 
82
70
  ## Documentation
@@ -98,6 +86,8 @@ uv add xloft
98
86
  - **NamedTuple**
99
87
 
100
88
  ```python
89
+ """This class imitates the behavior of the `named tuple`."""
90
+
101
91
  from xloft import NamedTuple
102
92
 
103
93
 
@@ -155,30 +145,68 @@ nt["y"] = "Hi" # => TypeError
155
145
  nt["_id"] = "new_id" # => TypeError
156
146
  nt["z"] = [1, 2, 3] # => TypeError
157
147
 
158
- nt.x = 20 # => raise: AttributeDoesNotSetValue
159
- nt.y = "Hi" # => raise: AttributeDoesNotSetValue
160
- nt._id = "new_id" # => raise: AttributeDoesNotSetValue
161
- nt.z = [1, 2, 3] # => raise: AttributeDoesNotSetValue
148
+ nt.x = 20 # => raise: AttributeDoesNotSetValueError
149
+ nt.y = "Hi" # => raise: AttributeDoesNotSetValueError
150
+ nt._id = "new_id" # => raise: AttributeDoesNotSetValueError
151
+ nt.z = [1, 2, 3] # => raise: AttributeDoesNotSetValueError
162
152
 
163
- del nt.x # => raise: AttributeCannotBeDelete
164
- del nt.y # => raise: AttributeCannotBeDelete
165
- del nt._id # => raise: AttributeCannotBeDelete
153
+ del nt.x # => raise: AttributeCannotBeDeleteError
154
+ del nt.y # => raise: AttributeCannotBeDeleteError
155
+ del nt._id # => raise: AttributeCannotBeDeleteError
166
156
  ```
167
157
 
168
- - **Human**
158
+ - **Converters**
169
159
 
170
160
  ```python
171
- from xloft import to_human_size
161
+ """Convert the number of bytes into a human-readable format."""
172
162
 
163
+ from xloft import to_human_size, int_to_roman, roman_to_int
164
+ # from xloft.converters import to_human_size, int_to_roman, roman_to_int
173
165
 
174
- s = to_human_size(200)
175
- print(s) # => 200 bytes
176
166
 
177
- s = to_human_size(1048576)
178
- print(s) # => 1 MB
167
+ to_human_size(200) # => 200 bytes
168
+ to_human_size(1048576) # => 1 MB
169
+ to_human_size(1048575) # => 1023.999 KB
170
+ #
171
+ int_to_roman(1994) # => MCMXCIV
172
+ roman_to_int("MCMXCIV") # => 1994
173
+ ```
179
174
 
180
- s = to_human_size(1048575)
181
- print(s) # => 1023.999 KB
175
+ - **ItIs**
176
+
177
+ ```python
178
+ """Check if a string is a number."""
179
+
180
+ from xloft import is_number, is_palindrome
181
+ # from xloft.itis import is_number, is_palindrome
182
+
183
+
184
+ is_number("") # => False
185
+ is_number(" ") # => False
186
+ is_number("1230.") # => False
187
+ is_number("0x5") # => False
188
+ is_number("0o5") # => False
189
+ is_number("-5.0") # => True
190
+ is_number("+5.0") # => True
191
+ is_number("5.0") # => True
192
+ is_number(".5") # => True
193
+ is_number("5.") # => True
194
+ is_number("3.4E+38") # => True
195
+ is_number("3.4E-38") # => True
196
+ is_number("1.7E+308") # => True
197
+ is_number("1.7E-308") # => True
198
+ is_number("-1.7976931348623157e+308") # => True
199
+ is_number("1.7976931348623157e+308") # => True
200
+ is_number("72028601076372765770200707816364342373431783018070841859646251155447849538676") # => True
201
+ is_number("-72028601076372765770200707816364342373431783018070841859646251155447849538676") # => True
202
+ #
203
+ is_palindrome("racecar") # True
204
+ is_palindrome("Go hang a salami, I'm a lasagna hog") # True
205
+ is_palindrome("22022022") # True
206
+ is_palindrome("Gene") # False
207
+ is_palindrome("123") # False
208
+ is_palindrome(123) # TypeError
209
+ is_palindrome("") # ValueError
182
210
  ```
183
211
 
184
212
  ## Changelog
@@ -0,0 +1,12 @@
1
+ xloft/__init__.py,sha256=hHQbBnIet5HUmjwp8R-6BCkGkFFSJYnXYz3INWFLwoc,1157
2
+ xloft/errors.py,sha256=FuI9IWgoD7J5Z34ijXhAGDBAEwBdbJOrHThEk1I33kI,988
3
+ xloft/itis.py,sha256=iQiPh4tvsHVqO5UIk3ZAcFQLS0KBPDogT6A0cNttjFk,1321
4
+ xloft/namedtuple.py,sha256=NFqgaArayECek36v7_rIfOhb8TgnXTY3XfSgwT_4yCY,5539
5
+ xloft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ xloft/converters/__init__.py,sha256=YxWzIqa9TgDTc2aSJnxniKWdaQF_GGOu--5k1yOR8mA,510
7
+ xloft/converters/human_size.py,sha256=-nxGQIz5_KMTSbOigYWPbXk4u2IAugTHpUYC_o2AM6E,1110
8
+ xloft/converters/roman.py,sha256=pqoM3xwaafzJtI6Fih--2GykM9JuOJ7sXaJbqZYlwmE,2293
9
+ xloft-0.9.6.dist-info/METADATA,sha256=Y6km_9v-wuHeeDbcGcR0TTNz9mNY3rE2MRzCOVLyiXg,7445
10
+ xloft-0.9.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
11
+ xloft-0.9.6.dist-info/licenses/LICENSE,sha256=mS0Wz0yGNB63gEcWEnuIb_lldDYV0sjRaO-o_GL6CWE,1074
12
+ xloft-0.9.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Gennady Kostyunin
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gennady Kostyunin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
xloft/human.py DELETED
@@ -1,64 +0,0 @@
1
- """A collection of instruments for converting data to format is convenient for humans.
2
-
3
- The module contains the following functions:
4
-
5
- - `to_human_size(size)` - Returns a humanized string: 200 bytes | 1 KB | 1.5 MB etc.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- __all__ = (
11
- "to_human_size",
12
- "get_cache_human_size",
13
- )
14
-
15
- import math
16
-
17
- # To caching the results from to_human_size method.
18
- _cache_human_size: dict[int, str] = {}
19
-
20
-
21
- def get_cache_human_size() -> dict[int, str]:
22
- """Get a copy of variable _cach_human_size.
23
-
24
- Hint: To tests.
25
- """
26
- return _cache_human_size.copy()
27
-
28
-
29
- def clean_cache_human_size() -> None:
30
- """Reset of variable _cach_human_size."""
31
- global _cache_human_size # noqa: PLW0603
32
- _cache_human_size = {}
33
-
34
-
35
- def to_human_size(n_bytes: int) -> str:
36
- """Convert number of bytes to readable format.
37
-
38
- Examples:
39
- >>> from xloft import to_human_size
40
- >>> to_human_size(200)
41
- 200 bytes
42
- >>> to_human_size(1048576)
43
- 1 MB
44
- >>> to_human_size(1048575)
45
- 1023.999 KB
46
-
47
- Args:
48
- n_bytes: The number of bytes.
49
-
50
- Returns:
51
- Returns a humanized string: 200 bytes | 1 KB | 1.5 MB etc.
52
- """
53
- result: str | None = _cache_human_size.get(n_bytes)
54
- if result is not None:
55
- return result
56
- idx: int = math.floor(math.log(n_bytes) / math.log(1024))
57
- ndigits: int = [0, 3, 6, 9, 12][idx]
58
- human_size: int | float = n_bytes if n_bytes < 1024 else abs(round(n_bytes / pow(1024, idx), ndigits))
59
- order = ["bytes", "KB", "MB", "GB", "TB"][idx]
60
- if math.modf(human_size)[0] == 0.0:
61
- human_size = int(human_size)
62
- result = f"{human_size} {order}"
63
- _cache_human_size[n_bytes] = result
64
- return result
@@ -1,9 +0,0 @@
1
- xloft/__init__.py,sha256=mf6z2-Tnl-CdYMa8mrI5MlTl48G0GQoYNNj2PMp8SsQ,419
2
- xloft/errors.py,sha256=hZcmF0QVVdvE5oM1jsXymRk_pPGgDSnUDM9wx9zJAYQ,895
3
- xloft/human.py,sha256=WQbVOKpOvzGot-c7f3xitbS05JUIQmj3dreGtRV6GA0,1792
4
- xloft/namedtuple.py,sha256=a_l3bZF-L2I7MGxuF2CXzAHgNai-Vyj6SY1ODwxs7TU,6856
5
- xloft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- xloft-0.5.1.dist-info/METADATA,sha256=o24kZAXLE4z4CaUnpSlqLbl-JihkCc0eXsP4mLsZQ-M,7079
7
- xloft-0.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- xloft-0.5.1.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
9
- xloft-0.5.1.dist-info/RECORD,,