dublib 0.28.0__tar.gz → 0.28.2__tar.gz

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.
Files changed (57) hide show
  1. {dublib-0.28.0/src/dublib.egg-info → dublib-0.28.2}/PKG-INFO +3 -1
  2. {dublib-0.28.0 → dublib-0.28.2}/pyproject.toml +10 -4
  3. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Templates/Bus.py +1 -1
  4. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Templates/__init__.py +2 -2
  5. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Terminalyzer/Command/Parser.py +66 -17
  6. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Terminalyzer/Helper.py +3 -2
  7. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Terminalyzer/__init__.py +7 -5
  8. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/TextStyler/__init__.py +1 -1
  9. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Validators.py +9 -12
  10. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/readline.py +4 -2
  11. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Engine/Configurator.py +6 -5
  12. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Engine/GetText.py +1 -1
  13. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Engine/Patcher.py +3 -4
  14. dublib-0.28.2/src/dublib/Exceptions/CLI/__init__.py +2 -0
  15. dublib-0.28.2/src/dublib/Exceptions/Engine/__init__.py +1 -0
  16. dublib-0.28.2/src/dublib/Exceptions/__init__.py +4 -0
  17. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Methods/Data.py +6 -4
  18. dublib-0.28.2/src/dublib/Methods/Decorators.py +53 -0
  19. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Methods/Filesystem.py +50 -35
  20. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Methods/System.py +2 -6
  21. dublib-0.28.2/src/dublib/Polyglot/__init__.py +2 -0
  22. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/TelebotUtils/Cache.py +31 -23
  23. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/TelebotUtils/Master/Decorators.py +1 -1
  24. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/TelebotUtils/Master/__init__.py +1 -2
  25. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/TelebotUtils/Users.py +9 -10
  26. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/WebRequestor.py +4 -4
  27. dublib-0.28.2/src/dublib/__init__.py +1 -0
  28. {dublib-0.28.0 → dublib-0.28.2/src/dublib.egg-info}/PKG-INFO +3 -1
  29. {dublib-0.28.0 → dublib-0.28.2}/src/dublib.egg-info/SOURCES.txt +0 -1
  30. {dublib-0.28.0 → dublib-0.28.2}/src/dublib.egg-info/requires.txt +2 -0
  31. dublib-0.28.0/src/dublib/Exceptions/CLI/__init__.py +0 -2
  32. dublib-0.28.0/src/dublib/Exceptions/Engine/__init__.py +0 -1
  33. dublib-0.28.0/src/dublib/Exceptions/__init__.py +0 -1
  34. dublib-0.28.0/src/dublib/Methods/Decorators.py +0 -23
  35. dublib-0.28.0/src/dublib/Polyglot/__init__.py +0 -2
  36. dublib-0.28.0/src/dublib/TelebotUtils/__init__.py +0 -3
  37. dublib-0.28.0/src/dublib/__init__.py +0 -1
  38. {dublib-0.28.0 → dublib-0.28.2}/LICENSE +0 -0
  39. {dublib-0.28.0 → dublib-0.28.2}/README.md +0 -0
  40. {dublib-0.28.0 → dublib-0.28.2}/setup.cfg +0 -0
  41. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/Terminalyzer/Command/Definition.py +0 -0
  42. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/TextStyler/Codes.py +0 -0
  43. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/TextStyler/Escapes.py +0 -0
  44. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/CLI/TextStyler/FastStyler.py +0 -0
  45. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Core.py +0 -0
  46. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Engine/Bus.py +0 -0
  47. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Exceptions/CLI/Terminalyzer.py +0 -0
  48. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Exceptions/CLI/Validators.py +0 -0
  49. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Exceptions/Engine/Bus.py +0 -0
  50. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Exceptions/TelebotUtils.py +0 -0
  51. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Exceptions/WebRequestor.py +0 -0
  52. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Polyglot/HTML.py +0 -0
  53. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/Polyglot/Markdown.py +0 -0
  54. {dublib-0.28.0 → dublib-0.28.2}/src/dublib/py.typed +0 -0
  55. {dublib-0.28.0 → dublib-0.28.2}/src/dublib.egg-info/dependency_links.txt +0 -0
  56. {dublib-0.28.0 → dublib-0.28.2}/src/dublib.egg-info/top_level.txt +0 -0
  57. {dublib-0.28.0 → dublib-0.28.2}/tests/test_Polyglot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dublib
3
- Version: 0.28.0
3
+ Version: 0.28.2
4
4
  Summary: Коллекция полезных модулей от DUB1401.
5
5
  Author-email: DUB1401 <vlad.milosta@outlook.com>
6
6
  License: This is free and unencumbered software released into the public domain.
@@ -52,6 +52,7 @@ Requires-Dist: httpx[http2]
52
52
  Requires-Dist: orjson
53
53
  Requires-Dist: prettytable
54
54
  Requires-Dist: requests
55
+ Requires-Dist: pathvalidate
55
56
  Requires-Dist: pyyaml
56
57
  Requires-Dist: pydantic
57
58
  Requires-Dist: validators
@@ -65,6 +66,7 @@ Requires-Dist: pyTelegramBotAPI; extra == "telebot"
65
66
  Provides-Extra: dev
66
67
  Requires-Dist: mypy; extra == "dev"
67
68
  Requires-Dist: pytest; extra == "dev"
69
+ Requires-Dist: ruff; extra == "dev"
68
70
  Requires-Dist: Sphinx; extra == "dev"
69
71
  Requires-Dist: sphinx-rtd-theme; extra == "dev"
70
72
  Dynamic: license-file
@@ -3,7 +3,7 @@ requires = ["setuptools"]
3
3
 
4
4
  [project]
5
5
  name = "dublib"
6
- version = "0.28.0"
6
+ version = "0.28.2"
7
7
  description = "Коллекция полезных модулей от DUB1401."
8
8
  authors = [
9
9
  {name = "DUB1401", email = "vlad.milosta@outlook.com"}
@@ -20,6 +20,7 @@ dependencies = [
20
20
  "orjson",
21
21
  "prettytable",
22
22
  "requests",
23
+ "pathvalidate",
23
24
  "pyyaml",
24
25
  "pydantic",
25
26
  "validators",
@@ -48,6 +49,7 @@ telebot = [
48
49
  dev = [
49
50
  "mypy",
50
51
  "pytest",
52
+ "ruff",
51
53
  "Sphinx",
52
54
  "sphinx-rtd-theme"
53
55
  ]
@@ -57,9 +59,13 @@ ignore_missing_imports = true
57
59
  check_untyped_defs = true
58
60
  non_interactive = true
59
61
  follow_imports = "silent"
60
- files = [
61
- "src"
62
- ]
62
+ files = ["src"]
63
+
64
+ [tool.ruff]
65
+ include = ["src/*"]
66
+
67
+ [tool.ruff.lint]
68
+ ignore = ["E701"]
63
69
 
64
70
  [project.urls]
65
71
  Documentation = "https://github.com/DUB1401/dublib/tree/main/docs"
@@ -1,5 +1,5 @@
1
1
  from ..TextStyler import Codes, TextStyler
2
- from .. import readline
2
+ from .. import readline as readline
3
3
 
4
4
  import enum
5
5
 
@@ -1,4 +1,4 @@
1
- from .. import readline
1
+ from .. import readline as readline
2
2
 
3
3
  #==========================================================================================#
4
4
  # >>>>> ШАБЛОНЫ ВВОДА-ВЫВОДА <<<<< #
@@ -29,6 +29,6 @@ def Confirmation(text: str, question: str | None = None, yes: str | None = None,
29
29
  InputLine = input(f"{text}\n{question} [{yes}/{no}]: ").strip().lower()
30
30
  if InputLine == yes.lower(): Response = True
31
31
  if InputLine == no.lower(): Response = False
32
- if Response != None: break
32
+ if Response is not None: break
33
33
 
34
34
  return Response
@@ -1,12 +1,18 @@
1
1
  from .... import Exceptions
2
2
 
3
- from typing import cast, Sequence, TYPE_CHECKING
3
+ from typing import cast, overload, Sequence, TypeVar, TYPE_CHECKING
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from .Definition import _Flag, _Key, Command
9
9
 
10
+ #==========================================================================================#
11
+ # >>>>> ПЕРЕМЕННЫЕ ТИПОВ <<<<< #
12
+ #==========================================================================================#
13
+
14
+ _EXPECTED_TYPE = TypeVar("_EXPECTED_TYPE", bool, float, int, Path, str, datetime)
15
+
10
16
  #==========================================================================================#
11
17
  # >>>>> ПРЕДСТАВЛЕНИЯ ОБРАБОТАННЫХ ПАРАМЕТРОВ <<<<< #
12
18
  #==========================================================================================#
@@ -135,9 +141,9 @@ class _ParsedCommandParameters:
135
141
 
136
142
  Result = list()
137
143
 
138
- for Sequence in (self.__Positions.values(), self.__BasePosition):
139
- for Parameter in Sequence:
140
- if type(Parameter) == required_type: Result.append(Parameter)
144
+ for CurrentSequence in (self.__Positions.values(), self.__BasePosition):
145
+ for Parameter in CurrentSequence:
146
+ if type(Parameter) is required_type: Result.append(Parameter)
141
147
 
142
148
  return tuple(Result)
143
149
 
@@ -259,25 +265,49 @@ class ParsedCommandData:
259
265
 
260
266
  return False
261
267
 
262
- def get_key_value(self, key: str, exception: bool = False) -> bool | float | int | Path | str | datetime | None:
268
+ @overload
269
+ def get_key_value(self, key: str, expected_type: type[_EXPECTED_TYPE], exception: bool = False) -> _EXPECTED_TYPE | None: ...
270
+
271
+ @overload
272
+ def get_key_value(self, key: str, expected_type: None = None, exception: bool = False) -> bool | float | int | Path | str | datetime | None: ...
273
+
274
+ def get_key_value(self, key: str, expected_type: type | None = None, exception: bool = False) -> bool | float | int | Path | str | datetime | None:
263
275
  """
264
276
  Возвращает значение активированного ключа.
265
277
 
266
278
  :param key: Название ключа.
267
279
  :type key: str
268
- :param exception: Указывает, нужно ли выбросить исключение при отсутствии ключа.
280
+ :param expected_type: Ожидаемый тип значения. Если тип не соответствует, будет выброшено исключение `TypeError`. Проверяются только значения, отличные от `None`.
281
+ :type expected_type: type[bool | float | int | Path | str | datetime]
282
+ :param exception: Указывает, нужно ли выбрасывать исключение при отсутствии ключа.
269
283
  :type exception: bool
270
- :raises KeyError: Выбрасывается в случае активации соответствующего параметра и запросе значения отсутствующего ключа.
271
284
  :return: Значение ключа или `None` при отсутствующем ключе.
272
285
  :rtype: bool | float | int | Path | str | datetime | None
286
+ :raises KeyError: Ключ не найден. Отключается параметром `exception`.
287
+ :raises TypeError: Ожидается другой тип данных.
273
288
  """
274
289
 
275
- for CurrentKey in self.__ParsedData.keys:
276
- if key == CurrentKey.name or key in CurrentKey.aliases: return CurrentKey.value
290
+ ValueToReturn: bool | float | int | Path | str | datetime | None = None
291
+ IsKeyFound: bool = False
277
292
 
278
- if exception: raise KeyError(key)
293
+ for CurrentKey in self.__ParsedData.keys:
294
+ if key == CurrentKey.name or key in CurrentKey.aliases:
295
+ ValueToReturn = CurrentKey.value
296
+ IsKeyFound = True
297
+ break
298
+
299
+ if not IsKeyFound:
300
+ if exception:
301
+ raise KeyError(key)
302
+ else:
303
+ return None
304
+
305
+ if expected_type and ValueToReturn is not None:
306
+ ReturnedType = type(ValueToReturn)
307
+ if ReturnedType is not expected_type:
308
+ raise TypeError(f"Expected \"{expected_type}\", but on key {ReturnedType}.")
279
309
 
280
- return None
310
+ return ValueToReturn
281
311
 
282
312
  def get_position_parameter(self, position_name: str) -> _ParsedArgument | _ParsedFlag | _ParsedKey | None:
283
313
  """
@@ -291,25 +321,44 @@ class ParsedCommandData:
291
321
  """
292
322
 
293
323
  return self.__ParsedData.positions[position_name]
294
-
295
- def get_position_value(self, position_name: str) -> bool | float | int | Path | str | datetime | None:
324
+
325
+ @overload
326
+ def get_position_value(self, position_name: str, expected_type: type[_EXPECTED_TYPE]) -> _EXPECTED_TYPE: ...
327
+
328
+ @overload
329
+ def get_position_value(self, position_name: str, expected_type: None = None) -> bool | float | int | Path | str | datetime | None: ...
330
+
331
+ def get_position_value(self, position_name: str, expected_type: type | None = None) -> bool | float | int | Path | str | datetime | None:
296
332
  """
297
333
  Для аргументов и ключей на позиции возвращает значение, для флагов – `True` при активации.
298
334
 
299
335
  :param position_name: Имя позиции.
300
336
  :type position_name: str
301
- :return: Параметр позиции или `None` при пустой позиции.
337
+ :param expected_type: Ожидаемый тип значения. Если тип не соответствует, будет выбрашено исключение `TypeError`.
338
+ :type expected_type: type[bool | float | int | Path | str | datetime]
339
+ :return: Параметр позиции или `None` при пустой позиции. Для флага возвращает основное имя флага.
302
340
  :rtype: bool | float | int | Path | str | datetime | None
303
341
  :raises KeyError: Позиция не обнаружена.
342
+ :raises TypeError: Ожидается другой тип данных.
304
343
  """
305
344
 
306
345
  ParsedParameter = self.get_position_parameter(position_name)
307
- if not ParsedParameter: return None
346
+ ValueToReturn: bool | float | int | Path | str | datetime | None = None
308
347
 
309
- if type(ParsedParameter) == _ParsedFlag: return True
348
+ if not ParsedParameter:
349
+ ValueToReturn = None
350
+ elif type(ParsedParameter) is _ParsedFlag:
351
+ ValueToReturn = ParsedParameter.name
310
352
  else:
311
353
  ParsedParameter = cast(_ParsedArgument | _ParsedKey, ParsedParameter)
312
- return ParsedParameter.value
354
+ ValueToReturn = ParsedParameter.value
355
+
356
+ if expected_type:
357
+ ReturnedType = type(ValueToReturn)
358
+ if ReturnedType is not expected_type:
359
+ raise TypeError(f"Expected \"{expected_type}\", but on position {ReturnedType}.")
360
+
361
+ return ValueToReturn
313
362
 
314
363
  def to_dict(self) -> dict:
315
364
  """
@@ -296,8 +296,9 @@ class Helper:
296
296
  #---> Генерация таблицы.
297
297
  #==========================================================================================#
298
298
  Tables = list()
299
+ Categories: list[str | None] = list(CommandsCategories.keys())
299
300
 
300
- for Category in CommandsCategories.keys():
301
+ for Category in Categories:
301
302
  HelpTable: dict[str, list] = {
302
303
  "Commands": [],
303
304
  "Descriptions": []
@@ -320,7 +321,7 @@ class Helper:
320
321
 
321
322
  TableObject.align = "l"
322
323
  TableString = TableObject.get_string()
323
- if TableObject.header: TableString = " " * 8 + TableString.lstrip()
324
+ if len(Categories) > 1: TableString = " " * 8 + TableString.lstrip()
324
325
  Tables.append(TableString)
325
326
 
326
327
  self.__Callback("\n\n".join(Tables))
@@ -1,6 +1,6 @@
1
1
  from .Command.Parser import _CommandParser, ParsedCommandData
2
+ from ..Validators import ValidableTypes as ValidableTypes
2
3
  from .Command.Definition import Command
3
- from ..Validators import ValidableTypes
4
4
  from .Helper import Helper
5
5
 
6
6
  from ...Core import LOGS_HANDLER
@@ -46,7 +46,7 @@ class Terminalyzer:
46
46
 
47
47
  for CurrentPosition in command.positions:
48
48
  if CurrentPosition.description and len(CurrentPosition.parameters) == 1 and CurrentPosition.parameters[0].description:
49
- LOGGER.warning(f"Command: \"{command.name}\". Parametr description suppressed by position description on \"{CurrentPosition.name}\".")
49
+ LOGGER.warning(f"Command: \"{command.name}\". Parameter description suppressed by position description on \"{CurrentPosition.name}\".")
50
50
 
51
51
  def __CheckCommandForEmptyPositions(self, command: Command):
52
52
  """
@@ -119,6 +119,7 @@ class Terminalyzer:
119
119
  :raises Exceptions.CLI.Terminalyzer.MultipleCommandDefinition: Множественное определение команды.
120
120
  """
121
121
 
122
+ commands = commands.copy()
122
123
  if not self.__CommandName: return None
123
124
 
124
125
  self.__ValidateCommandsDefinitions(commands)
@@ -133,8 +134,9 @@ class Terminalyzer:
133
134
  break
134
135
 
135
136
  if self.__Helper.is_enabled and CommandData and CommandData.name == "help":
136
- CommandName = cast(str, CommandData.arguments[0])
137
- if CommandData.arguments: self.__Helper.generate_help_command(commands, CommandName, CommandData.check_flag("-t"))
137
+ if CommandData.arguments:
138
+ CommandName = cast(str, CommandData.arguments[0])
139
+ self.__Helper.generate_help_command(commands, CommandName, CommandData.check_flag("-t"))
138
140
  else: self.__Helper.generate_help_list(commands)
139
141
 
140
142
  return CommandData
@@ -147,7 +149,7 @@ class Terminalyzer:
147
149
  :type parameters: Sequence[str] | None
148
150
  """
149
151
 
150
- self.__Input = input or sys.argv[1:]
152
+ self.__Input = list(input) if input else sys.argv[1:]
151
153
 
152
154
  self.__CommandName = None
153
155
  self.__Parameters: tuple = tuple()
@@ -1,6 +1,6 @@
1
1
  from ...Methods.Data import ToSequence
2
2
 
3
- from .FastStyler import FastStyler
3
+ from .FastStyler import FastStyler as FastStyler
4
4
  from . import Codes, Escapes
5
5
 
6
6
  from types import MappingProxyType
@@ -6,6 +6,7 @@ from datetime import datetime
6
6
  from pathlib import Path
7
7
  from enum import Enum
8
8
 
9
+ from pathvalidate import is_valid_filepath
9
10
  import dateparser
10
11
  import validators
11
12
 
@@ -149,7 +150,7 @@ class Validator_Base64(BaseValidator[str]):
149
150
 
150
151
  try:
151
152
  Result = validators.base64(value)
152
- if type(Result) == bool: return Result
153
+ if type(Result) is bool: return Result
153
154
  except validators.ValidationError: pass
154
155
 
155
156
  return False
@@ -216,7 +217,7 @@ class Validator_Datetime(BaseValidator[datetime]):
216
217
  try:
217
218
  dateparser.parse(value)
218
219
  return True
219
- except: pass
220
+ except (ValueError, TypeError): pass
220
221
 
221
222
  return False
222
223
 
@@ -248,7 +249,7 @@ class Validator_Email(BaseValidator[str]):
248
249
 
249
250
  try:
250
251
  Result = validators.email(value)
251
- if type(Result) == bool: return Result
252
+ if type(Result) is bool: return Result
252
253
  except validators.ValidationError: pass
253
254
 
254
255
  return False
@@ -337,7 +338,7 @@ class Validator_IPv4(BaseValidator[str]):
337
338
 
338
339
  try:
339
340
  Result = validators.ipv4(value)
340
- if type(Result) == bool: return Result
341
+ if type(Result) is bool: return Result
341
342
  except validators.ValidationError: pass
342
343
 
343
344
  return False
@@ -370,7 +371,7 @@ class Validator_IPv6(BaseValidator[str]):
370
371
 
371
372
  try:
372
373
  Result = validators.ipv6(value)
373
- if type(Result) == bool: return Result
374
+ if type(Result) is bool: return Result
374
375
  except validators.ValidationError: pass
375
376
 
376
377
  return False
@@ -439,10 +440,7 @@ class Validator_Path(BaseValidator[Path]):
439
440
  :rtype: bool
440
441
  """
441
442
 
442
- try:
443
- Path(value)
444
- return True
445
- except: return False
443
+ return is_valid_filepath(value)
446
444
 
447
445
  class Validator_UnsignedInteger(BaseValidator[int]):
448
446
 
@@ -500,7 +498,7 @@ class Validator_URL(BaseValidator[str]):
500
498
 
501
499
  try:
502
500
  Result = validators.url(value)
503
- if type(Result) == bool: return Result
501
+ if type(Result) is bool: return Result
504
502
  except validators.ValidationError: pass
505
503
 
506
504
  return False
@@ -531,8 +529,7 @@ class Validator_ValidPath(BaseValidator[Path]):
531
529
  :rtype: bool
532
530
  """
533
531
 
534
- try: return Path(value).exists()
535
- except: return False
532
+ return Path(value).exists()
536
533
 
537
534
  #==========================================================================================#
538
535
  # >>>>> ПЕРЕЧИСЛЕНИЕ ВАЛИДАТОРОВ <<<<< #
@@ -2,9 +2,11 @@
2
2
 
3
3
  import sys
4
4
 
5
- try: import readline
5
+ try: import readline as readline
6
6
  except ImportError:
7
7
 
8
8
  if sys.platform == "win32":
9
9
  try: import pyreadline3 as readline
10
- except ImportError: pass
10
+ except ImportError: pass
11
+
12
+ __all__ = ["readline"]
@@ -17,7 +17,7 @@ class Config:
17
17
  #==========================================================================================#
18
18
 
19
19
  __EXCEPTION_DISABLED = object()
20
- __INSTANCES: "dict[PathLike, Config]" = dict()
20
+ __INSTANCES: "dict[str, Config]" = dict()
21
21
 
22
22
  #==========================================================================================#
23
23
  # >>>>> СВОЙСТВА <<<<< #
@@ -84,18 +84,19 @@ class Config:
84
84
  if args[0] not in cls.__INSTANCES:
85
85
  Instance = super().__new__(cls)
86
86
  Instance._IS_INITIALIZED = False
87
- cls.__INSTANCES[args[0]] = Instance
87
+ ConfigPath = Path(args[0])
88
+ cls.__INSTANCES[ConfigPath.as_posix()] = Instance
88
89
 
89
90
  return cls.__INSTANCES[args[0]]
90
91
 
91
- def __init__(self, path: PathLike):
92
+ def __init__(self, path: str | PathLike[str]):
92
93
  """
93
94
  Контейнер конфигурации.
94
95
 
95
96
  Может работать с файлами JSON и YAML. Определение происходит по расширению файла, в противном случае предпочтение отдаётся JSON.
96
97
 
97
98
  :param path: Путь к файлу параметров. На данный момент поддерживается только JSON.
98
- :type path: PathLike
99
+ :type path: str | PathLike[str]
99
100
  """
100
101
 
101
102
  if self._IS_INITIALIZED: return
@@ -248,7 +249,7 @@ class Config:
248
249
  def unload(self):
249
250
  """Выгружает контейнер из памяти."""
250
251
 
251
- del self.__INSTANCES[self.path]
252
+ del self.__INSTANCES[self.path.as_posix()]
252
253
 
253
254
  def validate(self, model: type[BaseModel] | None = None):
254
255
  """
@@ -58,7 +58,7 @@ class GetText:
58
58
  """
59
59
 
60
60
  if languages:
61
- if type(languages) == str: languages = [languages]
61
+ if type(languages) is str: languages = [languages]
62
62
 
63
63
  try: return gettext.translation(GetText._DOMAIN, GetText._PATH, languages = languages).gettext(message)
64
64
  except FileNotFoundError: return GetText.gettext(message)
@@ -1,5 +1,4 @@
1
1
  from ..Methods.Filesystem import ReadTextFile, WriteTextFile
2
- from ..Methods.Data import ToSequence
3
2
 
4
3
  from pathlib import Path
5
4
  from os import PathLike
@@ -34,12 +33,12 @@ class Patch:
34
33
  # >>>>> МЕТОДЫ <<<<< #
35
34
  #==========================================================================================#
36
35
 
37
- def __init__(self, path: PathLike):
36
+ def __init__(self, path: str | PathLike[str]):
38
37
  """
39
38
  Патч.
40
39
 
41
40
  :param path: Путь к файлу.
42
- :type path: PathLike
41
+ :type path: str | PathLike[str]
43
42
  """
44
43
 
45
44
  self.__Path = Path(path)
@@ -93,7 +92,7 @@ class Patch:
93
92
  :type count: int | None
94
93
  """
95
94
 
96
- if count == None: count = -1
95
+ if count is None: count = -1
97
96
  self.__TextLines = self.text.replace(old, new, count).split("\n")
98
97
 
99
98
  def replace_by_regex(self, regex: str, text: str, count: int = 0):
@@ -0,0 +1,2 @@
1
+ from . import Terminalyzer as Terminalyzer
2
+ from . import Validators as Validators
@@ -0,0 +1 @@
1
+ from . import Bus as Bus
@@ -0,0 +1,4 @@
1
+ from . import CLI as CLI
2
+ from . import Engine as Engine
3
+ from . import TelebotUtils as TelebotUtils
4
+ from . import WebRequestor as WebRequestor
@@ -18,9 +18,9 @@ def Copy(data: Any) -> Any:
18
18
  :return: Копия объекта.
19
19
  :rtype: Any
20
20
  """
21
-
21
+
22
22
  try: return orjson.loads(orjson.dumps(data))
23
- except: return copy.deepcopy(data)
23
+ except (orjson.JSONDecodeError, orjson.JSONEncodeError): return copy.deepcopy(data)
24
24
 
25
25
  def StringifyFloat(number: float, round_factor: int = 2) -> str:
26
26
  """
@@ -47,7 +47,7 @@ def StringToBool(value: str, literals: Sequence[str] = ("false", "0")) -> bool:
47
47
 
48
48
  :param value: Преобразуемая строка.
49
49
  :type value: str
50
- :param literals: Набор строк, интерпретируемых как `False`.
50
+ :param literals: Набор строк, интерпретируемых как `False`. Нечувствителен к регистру.
51
51
  :type literals: Sequence[str]
52
52
  :return: Результирующее значение.
53
53
  :rtype: bool
@@ -59,8 +59,10 @@ def StringToBool(value: str, literals: Sequence[str] = ("false", "0")) -> bool:
59
59
 
60
60
  @overload
61
61
  def ToSequence(value: Any, target_type: type[list]) -> list: ...
62
+
62
63
  @overload
63
64
  def ToSequence(value: Any, target_type: type[set]) -> set: ...
65
+
64
66
  @overload
65
67
  def ToSequence(value: Any, target_type: type[tuple] = ...) -> tuple: ...
66
68
 
@@ -76,7 +78,7 @@ def ToSequence(value: Any, target_type: type[list | set | tuple] = tuple) -> lis
76
78
  :rtype: list | set | tuple
77
79
  """
78
80
 
79
- if type(value) == target_type: return value
81
+ if type(value) is target_type: return value
80
82
  if type(value) in (list, set, tuple): return target_type(value)
81
83
 
82
84
  return target_type((value,))
@@ -0,0 +1,53 @@
1
+ from ..CLI.TextStyler import FastStyler
2
+
3
+ import functools
4
+ import warnings
5
+
6
+ def deprecated(message: str | None = None):
7
+ """
8
+ Декоратор: помечает функцию как устаревшую.
9
+
10
+ :param message: Сопровождающее сообщение.
11
+ :type message: str | None
12
+ """
13
+
14
+ def decorator(function):
15
+
16
+ @functools.wraps(function)
17
+ def Wrapper(*args, **kwargs):
18
+ Message = f" {message}" or ""
19
+ Function = FastStyler(function.__name__).decorate.bold
20
+ warnings.warn(f"{Function} is deprecated.{Message}", DeprecationWarning, stacklevel = 2)
21
+
22
+ return function(*args, **kwargs)
23
+
24
+ return Wrapper
25
+
26
+ return decorator
27
+
28
+ def run_before_method(method_name: str):
29
+ """
30
+ Декоратор: запускает публичный или защищённый метод, не принимающий аргументов, перед выполнением метода экземпляра класса. Запущенный метод имеет доступ к экземпляру.
31
+
32
+ :param method_name: Имя метода.
33
+ :type method_name: str
34
+ """
35
+
36
+ if method_name.startswith("__"):
37
+ raise ValueError("Only public and protected methods supported in @run_befor_method decorator.")
38
+
39
+ def decorator(function):
40
+
41
+ @functools.wraps(function)
42
+ def Wrapper(*args, **kwargs):
43
+ if args:
44
+ Object = args[0]
45
+ Method = getattr(Object, method_name, None)
46
+ if Method and callable(Method):
47
+ Method()
48
+
49
+ return function(*args, **kwargs)
50
+
51
+ return Wrapper
52
+
53
+ return decorator