gengineapi 0.1.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.
gengineapi/__init__.py ADDED
@@ -0,0 +1,43 @@
1
+ """
2
+ G-Engine API Client - Асинхронный модульный клиент для взаимодействия с API G-Engine.
3
+
4
+ Предоставляет удобный интерфейс для работы с платежами, финансами,
5
+ пользователями и другими ресурсами API G-Engine.
6
+ """
7
+
8
+ from .client import GEngineClient
9
+ from .config import GEngineConfig
10
+ from .exceptions import (
11
+ ApiAuthError,
12
+ ApiConnectionError,
13
+ ApiError,
14
+ ApiForbiddenError,
15
+ ApiParsingError,
16
+ ApiResourceNotFoundError,
17
+ ApiServerError,
18
+ ApiServiceUnavailableError,
19
+ ApiTimeoutError,
20
+ ApiValidationError,
21
+ )
22
+ from .http import AsyncHttpClient
23
+
24
+ __version__ = "1.0.0"
25
+ __all__ = [
26
+ # Основной клиент
27
+ 'GEngineClient',
28
+ # Конфигурация
29
+ 'GEngineConfig',
30
+ # HTTP клиент
31
+ 'AsyncHttpClient',
32
+ # Исключения
33
+ 'ApiError',
34
+ 'ApiConnectionError',
35
+ 'ApiTimeoutError',
36
+ 'ApiAuthError',
37
+ 'ApiForbiddenError',
38
+ 'ApiValidationError',
39
+ 'ApiResourceNotFoundError',
40
+ 'ApiServerError',
41
+ 'ApiServiceUnavailableError',
42
+ 'ApiParsingError',
43
+ ]
gengineapi/client.py ADDED
@@ -0,0 +1,121 @@
1
+ """
2
+ Основной класс клиента API G-Engine.
3
+
4
+ Предоставляет интерфейс для взаимодействия со всеми модулями API
5
+ и управления соединением.
6
+ """
7
+ import logging
8
+ from typing import Optional
9
+
10
+ from .http import AsyncHttpClient
11
+ from .modules import (
12
+ AuthModule,
13
+ CurrenciesModule,
14
+ FinancesModule,
15
+ PaymentsModule,
16
+ TransactionsModule,
17
+ UsersModule,
18
+ )
19
+
20
+
21
+ class GEngineClient:
22
+ """
23
+ Клиент для API G-Engine.
24
+
25
+ Предоставляет доступ ко всем модулям API через единый интерфейс
26
+ и управляет жизненным циклом HTTP-соединения.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ base_url: str,
32
+ jwt_token: Optional[str] = None,
33
+ timeout: int = 30,
34
+ max_retries: int = 3,
35
+ logger: Optional[logging.Logger] = None,
36
+ proxy: Optional[str] = None,
37
+ ) -> None:
38
+ """
39
+ Инициализирует клиент API G-Engine.
40
+
41
+ Args:
42
+ base_url: Базовый URL для API
43
+ jwt_token: JWT токен для аутентификации (если уже есть)
44
+ timeout: Таймаут для запросов в секундах (по умолчанию 30)
45
+ max_retries: Максимальное количество повторных попыток (по умолчанию 3)
46
+ logger: Логгер для записи информации (опционально)
47
+ proxy: Прокси для запросов в формате 'socks5://user:pass@host:port' (опционально)
48
+ """
49
+ self.logger = logger or logging.getLogger(__name__)
50
+
51
+ if jwt_token:
52
+ self.logger.info("Клиент инициализирован с существующим JWT токеном")
53
+
54
+ if proxy:
55
+ self.logger.info(f"Клиент будет использовать прокси: {proxy}")
56
+
57
+ # Инициализация HTTP-клиента
58
+ self.http_client = AsyncHttpClient(
59
+ base_url=base_url,
60
+ jwt_token=jwt_token,
61
+ timeout=timeout,
62
+ max_retries=max_retries,
63
+ logger=self.logger,
64
+ proxy=proxy,
65
+ )
66
+
67
+ # Инициализация модулей API
68
+ self.auth = AuthModule(http_client=self.http_client, logger=self.logger)
69
+ self.payments = PaymentsModule(http_client=self.http_client, logger=self.logger)
70
+ self.finances = FinancesModule(http_client=self.http_client, logger=self.logger)
71
+ self.users = UsersModule(http_client=self.http_client, logger=self.logger)
72
+ self.transactions = TransactionsModule(http_client=self.http_client, logger=self.logger)
73
+ self.currencies = CurrenciesModule(http_client=self.http_client, logger=self.logger)
74
+
75
+ async def close(self) -> None:
76
+ """
77
+ Закрывает соединение с API.
78
+
79
+ Должен быть вызван при завершении работы с клиентом.
80
+ """
81
+ await self.http_client.close()
82
+ self.logger.info("Соединение с API закрыто")
83
+
84
+ async def __aenter__(self) -> "GEngineClient":
85
+ """
86
+ Поддержка контекстного менеджера (async with).
87
+
88
+ Returns:
89
+ GEngineClient: Экземпляр клиента API
90
+ """
91
+ return self
92
+
93
+ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
94
+ """
95
+ Закрывает соединение при выходе из контекстного менеджера.
96
+ """
97
+ await self.close()
98
+
99
+ def update_token(self, jwt_token: str) -> None:
100
+ """
101
+ Обновляет JWT токен для аутентификации.
102
+
103
+ Args:
104
+ jwt_token: Новый JWT токен
105
+ """
106
+ self.http_client.update_token(jwt_token)
107
+ self.logger.info("JWT токен обновлен")
108
+
109
+ def update_proxy(self, proxy: Optional[str] = None) -> None:
110
+ """
111
+ Обновляет настройки прокси.
112
+
113
+ Args:
114
+ proxy: Новый прокси в формате 'socks5://user:pass@host:port' или None для отключения прокси
115
+ """
116
+ if hasattr(self.http_client, 'update_proxy'):
117
+ self.http_client.update_proxy(proxy)
118
+ if proxy:
119
+ self.logger.info(f"Прокси обновлен: {proxy}")
120
+ else:
121
+ self.logger.info("Прокси отключен")
gengineapi/config.py ADDED
@@ -0,0 +1,242 @@
1
+ """
2
+ Модуль конфигурации для клиента G-Engine API.
3
+
4
+ Предоставляет класс для централизованной настройки параметров клиента
5
+ и возможность повторного использования клиента без необходимости
6
+ каждый раз указывать параметры.
7
+ """
8
+ import os
9
+ import logging
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Optional, Dict, Any, ClassVar
13
+
14
+ from .client import GEngineClient
15
+
16
+
17
+ class GEngineConfig:
18
+ """
19
+ Класс-конфигурация для G-Engine клиента.
20
+
21
+ Позволяет настроить параметры клиента один раз и затем использовать
22
+ их для создания экземпляров клиента без повторного указания параметров.
23
+
24
+ Attributes:
25
+ base_url: Базовый URL для API
26
+ jwt_token: JWT токен для аутентификации
27
+ timeout: Таймаут для запросов в секундах
28
+ max_retries: Максимальное количество повторных попыток
29
+ logger: Логгер для записи информации
30
+ proxy: Прокси для запросов (например, 'socks5://user:pass@host:port')
31
+
32
+ Class Attributes:
33
+ _instance: Глобальный экземпляр клиента (для повторного использования)
34
+ """
35
+ # Настройки по умолчанию
36
+ base_url: ClassVar[str] = "https://api.example.com/api/v2"
37
+ jwt_token: ClassVar[Optional[str]] = None
38
+ timeout: ClassVar[int] = 30
39
+ max_retries: ClassVar[int] = 3
40
+ logger: ClassVar[Optional[logging.Logger]] = None
41
+ proxy: ClassVar[Optional[str]] = None
42
+
43
+ # Глобальный экземпляр клиента
44
+ _instance: ClassVar[Optional[GEngineClient]] = None
45
+
46
+ @classmethod
47
+ def setup(cls,
48
+ base_url: Optional[str] = None,
49
+ jwt_token: Optional[str] = None,
50
+ timeout: Optional[int] = None,
51
+ max_retries: Optional[int] = None,
52
+ logger: Optional[logging.Logger] = None,
53
+ proxy: Optional[str] = None) -> None:
54
+ """
55
+ Настраивает параметры клиента по умолчанию.
56
+
57
+ Args:
58
+ base_url: Базовый URL для API (опционально)
59
+ jwt_token: JWT токен для аутентификации (опционально)
60
+ timeout: Таймаут для запросов в секундах (опционально)
61
+ max_retries: Максимальное количество повторных попыток (опционально)
62
+ logger: Логгер для записи информации (опционально)
63
+ proxy: Прокси для запросов в формате 'socks5://user:pass@host:port' (опционально)
64
+ """
65
+ if base_url:
66
+ cls.base_url = base_url
67
+ if jwt_token:
68
+ cls.jwt_token = jwt_token
69
+ if timeout:
70
+ cls.timeout = timeout
71
+ if max_retries:
72
+ cls.max_retries = max_retries
73
+ if logger:
74
+ cls.logger = logger
75
+ if proxy is not None: # Проверяем None, чтобы можно было передать пустую строку для отключения прокси
76
+ cls.proxy = proxy
77
+
78
+ # Если есть активный глобальный клиент, закрываем его
79
+ if cls._instance:
80
+ import asyncio
81
+ try:
82
+ # Пытаемся закрыть клиент синхронно, если мы в событийном цикле
83
+ asyncio.get_event_loop().create_task(cls.reset())
84
+ except RuntimeError:
85
+ # Если мы не в событийном цикле, просто отмечаем инстанс как None
86
+ # Реальное закрытие произойдет при следующем использовании
87
+ cls._instance = None
88
+
89
+ @classmethod
90
+ def load_from_env(cls) -> None:
91
+ """
92
+ Загружает настройки из переменных окружения.
93
+
94
+ Ищет следующие переменные:
95
+ - GENGINE_BASE_URL: Базовый URL для API
96
+ - GENGINE_TOKEN: JWT токен для аутентификации
97
+ - GENGINE_TIMEOUT: Таймаут для запросов в секундах
98
+ - GENGINE_MAX_RETRIES: Максимальное количество повторных попыток
99
+ - GENGINE_PROXY: Прокси для запросов
100
+ """
101
+ base_url = os.environ.get("GENGINE_BASE_URL")
102
+ jwt_token = os.environ.get("GENGINE_TOKEN")
103
+ timeout_str = os.environ.get("GENGINE_TIMEOUT")
104
+ max_retries_str = os.environ.get("GENGINE_MAX_RETRIES")
105
+ proxy = os.environ.get("GENGINE_PROXY")
106
+
107
+ # Преобразуем строковые значения в числа, если они есть
108
+ timeout = int(timeout_str) if timeout_str and timeout_str.isdigit() else None
109
+ max_retries = int(max_retries_str) if max_retries_str and max_retries_str.isdigit() else None
110
+
111
+ cls.setup(
112
+ base_url=base_url,
113
+ jwt_token=jwt_token,
114
+ timeout=timeout,
115
+ max_retries=max_retries,
116
+ proxy=proxy,
117
+ )
118
+
119
+ @classmethod
120
+ def load_from_file(cls, file_path: str) -> None:
121
+ """
122
+ Загружает настройки из JSON-файла.
123
+
124
+ Args:
125
+ file_path: Путь к файлу настроек
126
+
127
+ Raises:
128
+ FileNotFoundError: Если файл не существует
129
+ json.JSONDecodeError: Если файл содержит некорректный JSON
130
+ """
131
+ path = Path(file_path)
132
+ if not path.exists():
133
+ raise FileNotFoundError(f"Файл конфигурации не найден: {file_path}")
134
+
135
+ with open(path, "r") as f:
136
+ config = json.load(f)
137
+
138
+ cls.setup(
139
+ base_url=config.get("base_url"),
140
+ jwt_token=config.get("jwt_token"),
141
+ timeout=config.get("timeout"),
142
+ max_retries=config.get("max_retries"),
143
+ proxy=config.get("proxy"),
144
+ )
145
+
146
+ @classmethod
147
+ def save_to_file(cls, file_path: str) -> None:
148
+ """
149
+ Сохраняет текущие настройки в JSON-файл.
150
+
151
+ Args:
152
+ file_path: Путь к файлу для сохранения настроек
153
+ """
154
+ config = {
155
+ "base_url": cls.base_url,
156
+ "jwt_token": cls.jwt_token,
157
+ "timeout": cls.timeout,
158
+ "max_retries": cls.max_retries,
159
+ "proxy": cls.proxy,
160
+ }
161
+
162
+ with open(file_path, "w") as f:
163
+ json.dump(config, f, indent=2)
164
+
165
+ @classmethod
166
+ def get_client_kwargs(cls) -> Dict[str, Any]:
167
+ """
168
+ Возвращает словарь с параметрами для создания клиента.
169
+
170
+ Returns:
171
+ Dict[str, Any]: Словарь с параметрами клиента
172
+ """
173
+ return {
174
+ "base_url": cls.base_url,
175
+ "jwt_token": cls.jwt_token,
176
+ "timeout": cls.timeout,
177
+ "max_retries": cls.max_retries,
178
+ "logger": cls.logger,
179
+ "proxy": cls.proxy,
180
+ }
181
+
182
+ @classmethod
183
+ def create_client(cls) -> GEngineClient:
184
+ """
185
+ Создает новый экземпляр клиента с текущими настройками.
186
+
187
+ Returns:
188
+ GEngineClient: Новый экземпляр клиента
189
+ """
190
+ return GEngineClient(**cls.get_client_kwargs())
191
+
192
+ @classmethod
193
+ async def get_client(cls) -> GEngineClient:
194
+ """
195
+ Возвращает глобальный экземпляр клиента или создает новый,
196
+ если глобальный экземпляр не существует.
197
+
198
+ Returns:
199
+ GEngineClient: Экземпляр клиента
200
+ """
201
+ if cls._instance is None:
202
+ cls._instance = cls.create_client()
203
+ return cls._instance
204
+
205
+ @classmethod
206
+ async def reset(cls) -> None:
207
+ """
208
+ Закрывает глобальный экземпляр клиента, если он существует.
209
+ """
210
+ if cls._instance:
211
+ await cls._instance.close()
212
+ cls._instance = None
213
+
214
+ @classmethod
215
+ async def update_token(cls, jwt_token: str) -> None:
216
+ """
217
+ Обновляет JWT токен в настройках и в глобальном экземпляре клиента.
218
+
219
+ Args:
220
+ jwt_token: Новый JWT токен
221
+ """
222
+ cls.jwt_token = jwt_token
223
+ if cls._instance:
224
+ cls._instance.update_token(jwt_token)
225
+
226
+ @classmethod
227
+ async def update_proxy(cls, proxy: Optional[str] = None) -> None:
228
+ """
229
+ Обновляет настройки прокси в конфигурации и в глобальном экземпляре клиента.
230
+
231
+ Args:
232
+ proxy: Новый прокси в формате 'socks5://user:pass@host:port' или None для отключения прокси
233
+ """
234
+ cls.proxy = proxy
235
+ if cls._instance:
236
+ # Если клиент поддерживает обновление прокси
237
+ if hasattr(cls._instance.http_client, 'update_proxy'):
238
+ cls._instance.http_client.update_proxy(proxy)
239
+ else:
240
+ # Иначе пересоздаем клиент
241
+ await cls.reset()
242
+ cls._instance = cls.create_client()
@@ -0,0 +1,125 @@
1
+ """
2
+ Модуль для обработки исключений API клиента G-Engine.
3
+
4
+ Содержит иерархию исключений для различных типов ошибок,
5
+ которые могут возникнуть при взаимодействии с API.
6
+ """
7
+ from typing import Any, Dict, Optional, Union
8
+
9
+
10
+ class ApiError(Exception):
11
+ """Базовое исключение для всех ошибок API."""
12
+
13
+ def __init__(
14
+ self,
15
+ message: str,
16
+ status_code: Optional[int] = None,
17
+ response_data: Optional[Dict[str, Any]] = None,
18
+ ) -> None:
19
+ """
20
+ Инициализация базового исключения API.
21
+
22
+ Args:
23
+ message: Сообщение об ошибке
24
+ status_code: HTTP-код ответа
25
+ response_data: Данные из ответа API
26
+ """
27
+ self.message = message
28
+ self.status_code = status_code
29
+ self.response_data = response_data or {}
30
+ super().__init__(self.message)
31
+
32
+ def __str__(self) -> str:
33
+ """Строковое представление исключения."""
34
+ parts = [self.message]
35
+ if self.status_code:
36
+ parts.append(f"Status code: {self.status_code}")
37
+ if self.response_data:
38
+ parts.append(f"Response data: {self.response_data}")
39
+ return " | ".join(parts)
40
+
41
+
42
+ # Ошибки соединения
43
+ class ApiConnectionError(ApiError):
44
+ """Исключение при ошибках соединения с API."""
45
+ pass
46
+
47
+
48
+ class ApiTimeoutError(ApiConnectionError):
49
+ """Исключение при превышении времени ожидания ответа от API."""
50
+ pass
51
+
52
+
53
+ # Ошибки авторизации
54
+ class ApiAuthError(ApiError):
55
+ """Исключение при ошибках авторизации."""
56
+ pass
57
+
58
+
59
+ class ApiForbiddenError(ApiAuthError):
60
+ """Исключение при отсутствии доступа к ресурсу."""
61
+ pass
62
+
63
+
64
+ # Ошибки валидации
65
+ class ApiValidationError(ApiError):
66
+ """Исключение при ошибках валидации запроса."""
67
+ pass
68
+
69
+
70
+ # Ошибки ресурсов
71
+ class ApiResourceNotFoundError(ApiError):
72
+ """Исключение при запросе несуществующего ресурса."""
73
+ pass
74
+
75
+
76
+ # Серверные ошибки
77
+ class ApiServerError(ApiError):
78
+ """Исключение при ошибках на стороне сервера."""
79
+ pass
80
+
81
+
82
+ class ApiServiceUnavailableError(ApiServerError):
83
+ """Исключение при недоступности сервиса."""
84
+ pass
85
+
86
+
87
+ # Ошибки парсинга
88
+ class ApiParsingError(ApiError):
89
+ """Исключение при ошибках парсинга ответа API."""
90
+ pass
91
+
92
+
93
+ def create_api_error(
94
+ status_code: int,
95
+ message: str = None,
96
+ response_data: Optional[Dict[str, Any]] = None
97
+ ) -> ApiError:
98
+ """
99
+ Фабрика для создания соответствующего исключения по коду статуса HTTP.
100
+
101
+ Args:
102
+ status_code: HTTP-код ответа
103
+ message: Сообщение об ошибке (опционально)
104
+ response_data: Данные из ответа API (опционально)
105
+
106
+ Returns:
107
+ ApiError: Соответствующее исключение
108
+ """
109
+ default_message = f"API вернул ошибку со статусом {status_code}"
110
+ message = message or default_message
111
+
112
+ error_classes = {
113
+ 400: ApiValidationError,
114
+ 401: ApiAuthError,
115
+ 403: ApiForbiddenError,
116
+ 404: ApiResourceNotFoundError,
117
+ 408: ApiTimeoutError,
118
+ 500: ApiServerError,
119
+ 502: ApiServerError,
120
+ 503: ApiServiceUnavailableError,
121
+ 504: ApiTimeoutError,
122
+ }
123
+
124
+ error_class = error_classes.get(status_code, ApiError)
125
+ return error_class(message=message, status_code=status_code, response_data=response_data)