pyquoks 1.3.2.1__py3-none-any.whl → 2.0.0__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.
pyquoks/localhost.py CHANGED
@@ -1,23 +1,33 @@
1
1
  from __future__ import annotations
2
2
  import typing
3
3
  import waitress, flask
4
+ import pyquoks.utils
4
5
 
5
6
 
6
- class ILocalhostFlask(flask.Flask):
7
+ class LocalhostFlask(flask.Flask, pyquoks.utils._HasRequiredAttributes):
7
8
  """
8
9
  Class for creating a simple localhost server
10
+
11
+ **Required attributes**::
12
+
13
+ _RULES = {"/": self.base_redirect}
14
+
15
+ Attributes:
16
+ _RULES: Dictionary with rules and functions
9
17
  """
10
18
 
19
+ _REQUIRED_ATTRIBUTES = {
20
+ "_RULES",
21
+ }
22
+
11
23
  _RULES: dict[str, typing.Callable]
12
- """
13
- Dictionary with rules and functions
14
-
15
- Example:
16
- _RULES = {"/": base_redirect}
17
- """
18
24
 
19
25
  def __init__(self, import_name: str) -> None:
20
- super().__init__(import_name)
26
+ self._check_attributes()
27
+
28
+ super().__init__(
29
+ import_name=import_name,
30
+ )
21
31
 
22
32
  for rule, view_func in self._RULES.items():
23
33
  self.add_url_rule(
@@ -28,6 +38,8 @@ class ILocalhostFlask(flask.Flask):
28
38
  def serve(self, port: int) -> None:
29
39
  """
30
40
  Starts this Flask application
41
+
42
+ :param port: Port number
31
43
  """
32
44
 
33
45
  waitress.serve(
pyquoks/models.py CHANGED
@@ -1,138 +1,136 @@
1
1
  from __future__ import annotations
2
+ import pyquoks.utils
2
3
 
3
4
 
4
- class IContainer:
5
- """
6
- Class for storing lists of models and another parameters
5
+ class Model:
7
6
  """
7
+ Class for storing parameters and models
8
8
 
9
- _ATTRIBUTES: set[str] | dict[str, set[str]] = None
10
- """
11
- Set of parameters that also can be stored one key deep
9
+ **Optional attributes**::
12
10
 
13
- Example:
14
11
  _ATTRIBUTES = {"beatmap_id", "score_id"}
15
12
 
16
- _ATTRIBUTES = {"attributes": {"max_combo", "star_rating"}}
17
- """
13
+ _OBJECTS = {"scores": ScoreModel}
18
14
 
19
- _DATA: dict[str, type] = None
15
+ Attributes:
16
+ _ATTRIBUTES: Set of parameters that stored in this model
17
+ _OBJECTS: Dictionary with attributes and their models
18
+ _data: Initial data that was passed into object
20
19
  """
21
- Dictionary with name and type of models stored in list inside ``json_data``
22
20
 
23
- Example:
24
- _DATA = {"scores": ScoreModel}
25
- """
21
+ _ATTRIBUTES: set[str] | None
26
22
 
27
- _OBJECTS: dict[str, type] = None
28
- """
29
- Dictionary with keys of ``json_data`` and types of models stored in a list inside
30
-
31
- Example:
32
- _OBJECTS = {"beatmaps": BeatmapModel, "scores": ScoreModel}
33
- """
23
+ _OBJECTS: dict[str, type] | None
34
24
 
35
25
  _data: dict
36
- """
37
- Initial data that was passed into object
38
- """
39
26
 
40
- def __init__(self, json_data: dict) -> None:
41
- self._data = json_data
27
+ def __init__(self, data: dict) -> None:
28
+ self._data = data
29
+
30
+ if hasattr(self, "_ATTRIBUTES"):
31
+ if isinstance(self._ATTRIBUTES, set):
32
+ for attribute in self._ATTRIBUTES:
33
+ setattr(self, attribute, self._data.get(attribute, None))
34
+ else:
35
+ self._ATTRIBUTES = None
36
+
37
+ if hasattr(self, "_OBJECTS"):
38
+ if isinstance(self._OBJECTS, dict):
39
+ for attribute, object_class in self._OBJECTS.items():
40
+ try:
41
+ setattr(self, attribute, object_class(self._data.get(attribute)))
42
+ except:
43
+ setattr(self, attribute, None)
44
+ else:
45
+ self._OBJECTS = None
42
46
 
43
- if isinstance(self._ATTRIBUTES, set):
44
- for name in self._ATTRIBUTES:
45
- setattr(self, name, self._data.get(name, None))
46
- elif isinstance(self._ATTRIBUTES, dict):
47
- for key, attributes in self._ATTRIBUTES.items():
48
- if isinstance(attributes, set):
49
- for name in attributes:
50
- try:
51
- setattr(self, name, self._data.get(key).get(name, None))
52
- except:
53
- setattr(self, name, None)
54
-
55
- if isinstance(self._DATA, dict):
56
- for name, data_class in self._DATA.items():
57
- try:
58
- setattr(self, name, [data_class(data) for data in self._data])
59
- except:
60
- setattr(self, name, None)
61
- elif isinstance(self._OBJECTS, dict):
62
- for name, data_class in self._OBJECTS.items():
63
- try:
64
- setattr(self, name, [data_class(data) for data in self._data.get(name)])
65
- except:
66
- setattr(self, name, None)
67
-
68
-
69
- class IModel:
70
- """
71
- Class for storing parameters and models
72
- """
73
47
 
74
- _ATTRIBUTES: set[str] | dict[str, set[str]] = None
48
+ class Container:
75
49
  """
76
- Set of parameters that also can be stored one key deep
50
+ Class for storing lists of models and another parameters
51
+
52
+ **Optional attributes**::
77
53
 
78
- Example:
79
54
  _ATTRIBUTES = {"beatmap_id", "score_id"}
80
55
 
81
- _ATTRIBUTES = {"attributes": {"max_combo", "star_rating"}}
82
- """
56
+ _OBJECTS = {"beatmap": BeatmapModel}
83
57
 
84
- _OBJECTS: dict[str, type] = None
85
- """
86
- Dictionary with attributes and their models
87
-
88
- Example:
89
- _OBJECTS = {"score": ScoreModel}
58
+ _DATA = {"scores": ScoreModel}
59
+
60
+ Attributes:
61
+ _ATTRIBUTES: Set of parameters that stored in this container
62
+ _OBJECTS: Dictionary with attributes and their models
63
+ _DATA: Dictionary with attribute and type of models stored in list
64
+ _data: Initial data that was passed into object
90
65
  """
91
66
 
67
+ _ATTRIBUTES: set[str] | None
68
+
69
+ _OBJECTS: dict[str, type] | None
70
+
71
+ _DATA: dict[str, type] | None
72
+
92
73
  _data: dict | list[dict]
93
- """
94
- Initial data that was passed into object
95
- """
96
74
 
97
- def __init__(self, json_data: dict | list[dict]) -> None:
98
- self._data = json_data
75
+ def __init__(self, data: dict | list[dict]) -> None:
76
+ self._data = data
77
+
78
+ if isinstance(self._data, dict):
79
+ if hasattr(self, "_ATTRIBUTES"):
80
+ if isinstance(self._ATTRIBUTES, set):
81
+ for attribute in self._ATTRIBUTES:
82
+ setattr(self, attribute, self._data.get(attribute, None))
83
+ else:
84
+ self._ATTRIBUTES = None
99
85
 
100
- if isinstance(self._ATTRIBUTES, set):
101
- for name in self._ATTRIBUTES:
102
- setattr(self, name, self._data.get(name, None))
103
- elif isinstance(self._ATTRIBUTES, dict):
104
- for key, attributes in self._ATTRIBUTES.items():
105
- if isinstance(attributes, set):
106
- for name in attributes:
86
+ if hasattr(self, "_OBJECTS"):
87
+ if isinstance(self._OBJECTS, dict):
88
+ for attribute, object_class in self._OBJECTS.items():
107
89
  try:
108
- setattr(self, name, self._data.get(key).get(name, None))
90
+ setattr(self, attribute, object_class(self._data.get(attribute)))
109
91
  except:
110
- setattr(self, name, None)
111
-
112
- if isinstance(self._OBJECTS, dict):
113
- for name, data_class in self._OBJECTS.items():
114
- try:
115
- setattr(self, name, data_class(self._data.get(name)))
116
- except:
117
- setattr(self, name, None)
92
+ setattr(self, attribute, None)
93
+ else:
94
+ self._OBJECTS = None
95
+ elif isinstance(self._data, list):
96
+ if hasattr(self, "_DATA"):
97
+ if isinstance(self._DATA, dict):
98
+ for attribute, object_class in self._DATA.items():
99
+ try:
100
+ setattr(self, attribute, [object_class(data) for data in self._data])
101
+ except:
102
+ setattr(self, attribute, None)
103
+ else:
104
+ self._DATA = None
118
105
 
119
106
 
120
- class IValues:
107
+ class Values(pyquoks.utils._HasRequiredAttributes):
121
108
  """
122
109
  Class for storing various parameters and values
123
- """
124
110
 
125
- _ATTRIBUTES: set[str] = None
126
- """
127
- Attributes that can be stored in this class
111
+ **Required attributes**::
128
112
 
129
- Example:
130
113
  _ATTRIBUTES = {"settings", "path"}
114
+
115
+ Attributes:
116
+ _ATTRIBUTES: Attributes that can be stored in this class
131
117
  """
132
118
 
119
+ _REQUIRED_ATTRIBUTES = {
120
+ "_ATTRIBUTES",
121
+ }
122
+
123
+ _ATTRIBUTES: set[str]
124
+
133
125
  def __init__(self, **kwargs) -> None:
134
- for name in self._ATTRIBUTES:
135
- setattr(self, name, kwargs.get(name, None))
126
+ self._check_attributes()
127
+
128
+ for attribute in self._ATTRIBUTES:
129
+ setattr(self, attribute, kwargs.get(attribute, getattr(self, attribute, None)))
136
130
 
137
131
  def update(self, **kwargs) -> None:
132
+ """
133
+ Updates provided attributes in object
134
+ """
135
+
138
136
  self.__init__(**kwargs)
pyquoks/test.py ADDED
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+ import unittest, types
3
+ import pyquoks.data, pyquoks.utils
4
+
5
+
6
+ class TestCase(unittest.TestCase, pyquoks.utils._HasRequiredAttributes):
7
+ """
8
+ Class for performing unit testing
9
+
10
+ **Required attributes**::
11
+
12
+ _MODULE_NAME = __name__
13
+
14
+ Attributes:
15
+ _MODULE_NAME: Name of the testing module
16
+ """
17
+
18
+ _REQUIRED_ATTRIBUTES = {
19
+ "_MODULE_NAME"
20
+ }
21
+
22
+ _MODULE_NAME: str
23
+
24
+ def __init__(self, *args, **kwargs) -> None:
25
+ self._check_attributes()
26
+
27
+ super().__init__(*args, **kwargs)
28
+
29
+ @classmethod
30
+ def setUpClass(cls) -> None:
31
+ cls._logger = pyquoks.data.LoggerService(
32
+ filename=__name__,
33
+ )
34
+
35
+ def _get_func_name(self, func_name: str) -> str:
36
+ return f"{self._MODULE_NAME}.{func_name}"
37
+
38
+ def assert_equal(
39
+ self,
40
+ func_name: str,
41
+ test_data: object,
42
+ test_expected: object,
43
+ ) -> None:
44
+ self._logger.info(
45
+ msg=(
46
+ f"{self._get_func_name(func_name)}:\n"
47
+ f"Data: {test_data}\n"
48
+ f"Expected: {test_expected}\n"
49
+ ),
50
+ )
51
+
52
+ try:
53
+ self.assertEqual(
54
+ first=test_data,
55
+ second=test_expected,
56
+ )
57
+ except Exception as exception:
58
+ self._logger.log_error(
59
+ exception=exception,
60
+ raise_again=True,
61
+ )
62
+
63
+ def assert_type(
64
+ self,
65
+ func_name: str,
66
+ test_data: object,
67
+ test_type: type | types.UnionType,
68
+ ) -> None:
69
+ self._logger.info(
70
+ msg=(
71
+ f"{self._get_func_name(func_name)}:\n"
72
+ f"Type: {type(test_data).__name__}\n"
73
+ f"Expected: {test_type.__name__}\n"
74
+ ),
75
+ )
76
+
77
+ try:
78
+ self.assertIsInstance(
79
+ obj=test_data,
80
+ cls=test_type,
81
+ )
82
+ except Exception as exception:
83
+ self._logger.log_error(
84
+ exception=exception,
85
+ raise_again=True,
86
+ )
pyquoks/utils.py CHANGED
@@ -2,17 +2,37 @@ from __future__ import annotations
2
2
  import sys, os
3
3
 
4
4
 
5
- def get_path(relative_path: str, only_abspath: bool = False) -> str:
5
+ class _HasRequiredAttributes:
6
6
  """
7
+ Assistive class for checking for required attributes
8
+
9
+ **Required attributes**::
10
+
11
+ _REQUIRED_ATTRIBUTES = {"_ATTRIBUTES", "_OBJECTS"}
12
+
13
+ Attributes:
14
+ _REQUIRED_ATTRIBUTES: Set of required attributes in the class
15
+ """
16
+
17
+ _REQUIRED_ATTRIBUTES: set[str]
18
+
19
+ def _check_attributes(self) -> None:
20
+ if hasattr(self, "_REQUIRED_ATTRIBUTES"):
21
+ for attribute in self._REQUIRED_ATTRIBUTES:
22
+ if not hasattr(self, attribute):
23
+ raise AttributeError(f"The required class attribute is not set! ({attribute})")
24
+
25
+
26
+ def get_path(relative_path: str, use_meipass: bool = False) -> str:
27
+ """
28
+ :param relative_path: Relative path of the file
29
+ :param use_meipass: Whether or not ``sys._MEIPASS`` should be used
7
30
  :return: Absolute path for provided relative path
8
31
  """
9
32
 
10
- try:
11
- # noinspection PyUnresolvedReferences
33
+ if use_meipass and hasattr(sys, "_MEIPASS"):
12
34
  base_path = sys._MEIPASS
13
- except:
35
+ else:
14
36
  base_path = os.path.abspath(".")
15
- finally:
16
- if only_abspath:
17
- base_path = os.path.abspath(".")
37
+
18
38
  return os.path.join(base_path, relative_path)
@@ -1,55 +1,56 @@
1
- Metadata-Version: 2.4
2
- Name: pyquoks
3
- Version: 1.3.2.1
4
- Summary: Пакет PyPI для часто используемых модулей в проектах diquoks
5
- Home-page: https://diquoks.ru
6
- Author: Denis Titovets
7
- Author-email: den232titovets@yandex.ru
8
- Project-URL: Bug Tracker, https://github.com/diquoks/pyquoks/issues
9
- Project-URL: Repository, https://github.com/diquoks/pyquoks
10
- Classifier: Programming Language :: Python :: 3
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: blinker==1.9.0
15
- Requires-Dist: certifi==2025.10.5
16
- Requires-Dist: charset-normalizer==3.4.4
17
- Requires-Dist: click==8.3.0
18
- Requires-Dist: colorama==0.4.6
19
- Requires-Dist: Flask==3.1.2
20
- Requires-Dist: idna==3.11
21
- Requires-Dist: itsdangerous==2.2.0
22
- Requires-Dist: Jinja2==3.1.6
23
- Requires-Dist: MarkupSafe==3.0.3
24
- Requires-Dist: pillow==12.0.0
25
- Requires-Dist: requests==2.32.5
26
- Requires-Dist: urllib3==2.5.0
27
- Requires-Dist: waitress==3.0.2
28
- Requires-Dist: Werkzeug==3.1.3
29
- Dynamic: license-file
30
-
31
- # pyquoks
32
-
33
- #### Пакет PyPI для часто используемых модулей в проектах diquoks
34
-
35
- ---
36
-
37
- ## Оглавление
38
-
39
- - [Контакты](#контакты)
40
- - [Связь с разработчиком](#связь-с-разработчиком)
41
- - [Прочие ссылки](#прочие-ссылки)
42
-
43
- ---
44
-
45
- ## Контакты
46
-
47
- #### Связь с разработчиком
48
-
49
- - [План разработки pyquoks](https://www.icloud.com/notes/0e0C-Bm4IkqXuBYqJrq00yhog)
50
- - [Telegram для связи](https://t.me/diquoks)
51
- - [Почта для связи](mailto:diquoks@yandex.ru)
52
-
53
- #### Прочие ссылки
54
-
55
- - [Telegram-канал с новостями](https://t.me/diquoks_channel)
1
+ Metadata-Version: 2.4
2
+ Name: pyquoks
3
+ Version: 2.0.0
4
+ Summary: Пакет PyPI для часто используемых модулей в проектах diquoks
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Denis Titovets
8
+ Author-email: den232titovets@yandex.ru
9
+ Requires-Python: >=3.13
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Dist: blinker (==1.9.0)
15
+ Requires-Dist: certifi (==2025.11.12)
16
+ Requires-Dist: charset-normalizer (==3.4.4)
17
+ Requires-Dist: click (==8.3.0)
18
+ Requires-Dist: colorama (==0.4.6)
19
+ Requires-Dist: flask (==3.1.2)
20
+ Requires-Dist: idna (==3.11)
21
+ Requires-Dist: itsdangerous (==2.2.0)
22
+ Requires-Dist: jinja2 (==3.1.6)
23
+ Requires-Dist: markupsafe (==3.0.3)
24
+ Requires-Dist: pillow (==12.0.0)
25
+ Requires-Dist: requests (==2.32.5)
26
+ Requires-Dist: urllib3 (==2.5.0)
27
+ Requires-Dist: waitress (==3.0.2)
28
+ Requires-Dist: werkzeug (==3.1.3)
29
+ Description-Content-Type: text/markdown
30
+
31
+ # pyquoks
32
+
33
+ #### Пакет PyPI для часто используемых модулей в проектах diquoks
34
+
35
+ ---
36
+
37
+ ## Оглавление
38
+
39
+ - [Контакты](#контакты)
40
+ - [Связь с разработчиком](#связь-с-разработчиком)
41
+ - [Прочие ссылки](#прочие-ссылки)
42
+
43
+ ---
44
+
45
+ ## Контакты
46
+
47
+ #### Связь с разработчиком
48
+
49
+ - [План разработки pyquoks](https://www.icloud.com/notes/0e0C-Bm4IkqXuBYqJrq00yhog)
50
+ - [Telegram для связи](https://t.me/diquoks)
51
+ - [Почта для связи](mailto:diquoks@yandex.ru)
52
+
53
+ #### Прочие ссылки
54
+
55
+ - [Telegram-канал с новостями](https://t.me/diquoks_channel)
56
+
@@ -0,0 +1,10 @@
1
+ pyquoks/__init__.py,sha256=r70_95sq0zntKp4Jjkk_0hgDMBzUGtCK8EGkkCA6k1c,143
2
+ pyquoks/data.py,sha256=cbiMsUf6lXkc3fWxya906HgCjdVNa-NKkgx_TJG0EiU,16239
3
+ pyquoks/localhost.py,sha256=-SG-VFoQ7gLksydlDFL3jrHwjNDurHkbXWB1rRxuFaA,1087
4
+ pyquoks/models.py,sha256=pJ165gZnc9eeQnLJhJ3om6nhL3c2UZ9THDu060N3V1w,4154
5
+ pyquoks/test.py,sha256=Dw4nVResnFatBQtFZnl1OKdIkVbmmeATEBRLOhfPcac,2219
6
+ pyquoks/utils.py,sha256=m1N32manBwqw2X4gZJ6czA2ywpF1WBLdd9SKNpzKnpY,1152
7
+ pyquoks-2.0.0.dist-info/licenses/LICENSE,sha256=eEd8UIYxvKUY7vqrV1XTFo70_FQdiW6o1fznseCXRJs,1095
8
+ pyquoks-2.0.0.dist-info/METADATA,sha256=GzbqbS_5FaiFYSWN_WcxEHtscrIz9z1yYPrZgdCSrII,1780
9
+ pyquoks-2.0.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
10
+ pyquoks-2.0.0.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1,10 +0,0 @@
1
- pyquoks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- pyquoks/data.py,sha256=lwdhIS2zls5UqN7hn4shL3NBHDUxDUk8R7CicbQqM20,15724
3
- pyquoks/localhost.py,sha256=2sl1CovYrnIzBYW-eKDIYOfi5bUsSVTAdQobzAHpIys,828
4
- pyquoks/models.py,sha256=kdduuHgh8RLe8_S5Lmv3jS6hqsZG-oYQhPTgk2C11ug,4171
5
- pyquoks/utils.py,sha256=lcHZyIFIflGMqAhDkqm8GWOUFwb2f-IHgIPZxEiHBz0,484
6
- pyquoks-1.3.2.1.dist-info/licenses/LICENSE,sha256=eEd8UIYxvKUY7vqrV1XTFo70_FQdiW6o1fznseCXRJs,1095
7
- pyquoks-1.3.2.1.dist-info/METADATA,sha256=FvyQo98t33VtfExAHVdUf1nCmOcQFsYcA9Wf3NY8Cz8,1804
8
- pyquoks-1.3.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- pyquoks-1.3.2.1.dist-info/top_level.txt,sha256=4Eqn44TCCp-D8V6To92e8-KKr49ZGf0dCcsdRQmPkhc,8
10
- pyquoks-1.3.2.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- pyquoks