Skip to content

Commit

Permalink
Refactor VkRequestsPool:
Browse files Browse the repository at this point in the history
- Make VkRequestsPool.method_one_param separated function: vk_request_one_param_pool
- Use actual error dict for RequestResult, instead of bool
  • Loading branch information
python273 committed Jun 22, 2018
1 parent e165e5f commit a9e425c
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 119 deletions.
42 changes: 28 additions & 14 deletions examples/requests_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def main():
vk_session = vk_api.VkApi(login, password)

try:
vk_session.auth()
vk_session.auth(token_only=True)
except vk_api.AuthError as error_msg:
print(error_msg)
return
Expand All @@ -35,6 +35,17 @@ def main():
# False - поэтому нельзя обратиться к результату
print(request_with_error.ok)

# Получить ошибку
print(request_with_error.error)

try:
""" Если запрос был завершен с ошибкой, то при обращении к результату
будет выбрасываться исключение
"""
_ = request_with_error.result
except vk_api.VkRequestsPoolException as e:
print('Error:', e)

""" Пример получения друзей у нескольких пользователей за один запрос
"""

Expand All @@ -52,25 +63,28 @@ def main():

print(friends)

""" Следующий пример - оптимизированная версия предыдущего
""" Следующий пример - более простая версия предыдущего
В friends будет записан тот же самый результат, что и в прошлом примере.
method_one_param можно использовать, когда запрос идет к одному методу,
и когда изменяется только один параметр. В данном случае это 'user_id'
vk_request_one_param_pool можно использовать, когда запрос идет к
одному методу и когда изменяется только один параметр. В данном случае
это 'user_id'.
Плюс не нужно вызывать .result для каждого ключа
Кроме того не нужно получать .result для каждого ключа.
"""
with vk_api.VkRequestsPool(vk_session) as pool:
friends = pool.method_one_param(
'friends.get', # Метод
key='user_id', # Изменяющийся параметр
values=[1, 183433824],
friends, errors = vk_api.vk_request_one_param_pool(
vk_session,
'friends.get', # Метод
key='user_id', # Изменяющийся параметр
values=[1, 3, 183433824],

# Параметры, которые будут в каждом запросе
default_values={'fields': 'photo'}
)
# Параметры, которые будут в каждом запросе
default_values={'fields': 'photo'}
)

print(friends)
print(errors)

print(friends.result)

if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion vk_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from .enums import *
from .exceptions import *
from .requests_pool import VkRequestsPool
from .requests_pool import VkRequestsPool, vk_request_one_param_pool
from .tools import VkTools
from .upload import VkUpload
from .vk_api import VkApi
Expand Down
6 changes: 6 additions & 0 deletions vk_api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,9 @@ class VkAudioUrlDecodeError(VkAudioException):

class VkToolsException(VkApiError):
pass


class VkRequestsPoolException(Exception):
def __init__(self, error, *args, **kwargs):
self.error = error
super(VkRequestsPoolException, self).__init__(*args, **kwargs)
189 changes: 85 additions & 104 deletions vk_api/requests_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@
import sys
from collections import namedtuple

from .utils import sjson_dumps
from .exceptions import VkRequestsPoolException
from .execute import VkFunction
from .utils import sjson_dumps

if sys.version_info.major == 2:
range = xrange


class VkRequestsPoolException(Exception):
pass


PoolRequest = namedtuple('PoolRequest', ['method', 'values', 'result'])


Expand Down Expand Up @@ -51,7 +48,13 @@ def result(self):
raise RuntimeError('Result is not available in `with` context')

if self._error:
raise VkRequestsPoolException('Got error while executing request')
raise VkRequestsPoolException(
self._error,
'Got error while executing request: [{}] {}'.format(
self.error['error_code'],
self.error['error_msg']
)
)

return self._result

Expand All @@ -74,28 +77,20 @@ class VkRequestsPool(object):
Служит как менеджер контекста: запросы к API добавляются в
открытый пул, и выполняются при его закрытии.
:param vk: Объект :class:`VkApi`
:param vk_session: Объект :class:`VkApi`
"""

__slots__ = ('vk', 'pool', 'one_param', 'execute_errors')
__slots__ = ('vk_session', 'pool')

def __init__(self, vk):
self.vk = vk
def __init__(self, vk_session):
self.vk_session = vk_session
self.pool = []
self.one_param = {}
self.execute_errors = []

def __enter__(self):
return self

def __exit__(self, *args, **kwargs):
if self.one_param:
self.execute_one_param()
else:
self.execute()

def get_execute_errors(self):
return self.execute_errors
self.execute()

def method(self, method, values=None):
"""
Expand All @@ -111,9 +106,6 @@ def method(self, method, values=None):
:rtype: RequestResult
"""

if self.one_param:
raise RuntimeError('One param mode does not work with self.method')

if values is None:
values = {}

Expand All @@ -122,44 +114,6 @@ def method(self, method, values=None):

return result

def method_one_param(self, method, key, values, default_values=None):
"""
Использовать, если изменяется значение только одного параметра.
Невозможно использовать вместе с :func:`method`.
Возвращаемое значение будет содержать результат после закрытия пула.
:param method: метод
:type method: str
:param default_values: одинаковые значения для запросов
:type default_values: dict
:param key: ключ изменяющегося параметра
:type key: str
:param values: список значений изменяющегося параметра (max: 25)
:type values: list
:rtype: RequestResult
"""

if not self.one_param and self.pool:
raise RuntimeError('One param mode does not work with self.method')

if default_values is None:
default_values = {}

self.one_param = {
'method': method,
'default': default_values,
'key': key,
'return': RequestResult()
}

self.pool = values

return self.one_param['return']

def execute(self):
for i in range(0, len(self.pool), 25):
cur_pool = self.pool[i:i + 25]
Expand All @@ -169,45 +123,24 @@ def execute(self):
if one_method:
value_list = [i.values for i in cur_pool]

response_raw = vk_one_method(self.vk, one_method, value_list)
response_raw = vk_one_method(
self.vk_session, one_method, value_list
)
else:
response_raw = vk_many_methods(self.vk, cur_pool)

response = response_raw['response']
response_errors = response_raw.get('execute_errors')

if response_errors:
self.execute_errors += response_errors[:-1]

for x, response in enumerate(response):
if response is not False:
cur_pool[x].result.result = response
else:
cur_pool[x].result.error = True

def execute_one_param(self):
result = {}

method = self.one_param['method']
default = self.one_param['default']
key = self.one_param['key']

for i in range(0, len(self.pool), 25):
cur_pool = self.pool[i:i + 25]

response_raw = vk_one_param(self.vk, method, cur_pool, default, key)
response_raw = vk_many_methods(self.vk_session, cur_pool)

response = response_raw['response']
response_errors = response_raw.get('execute_errors')
response_errors = response_raw.get('execute_errors', [])

if response_errors:
self.execute_errors += response_errors[:-1]
response_errors_iter = iter(response_errors)

for x, r in enumerate(response):
if r is not False:
result[cur_pool[x]] = r
for x, current_response in enumerate(response):
current_result = cur_pool[x].result

self.one_param['return'].result = result
if current_response is not False:
current_result.result = current_response
else:
current_result.error = next(response_errors_iter)


def check_one_method(pool):
Expand Down Expand Up @@ -242,6 +175,65 @@ def check_one_method(pool):
''')


def vk_many_methods(vk_session, pool):
requests = ','.join(
'API.{}({})'.format(i.method, sjson_dumps(i.values))
for i in pool
)

code = 'return [{}];'.format(requests)

return vk_session.method('execute', {'code': code}, raw=True)


def vk_request_one_param_pool(vk_session, method, key, values,
default_values=None):
"""
Использовать, если изменяется значение только одного параметра.
Невозможно использовать вместе с :func:`method`.
Возвращаемое значение будет содержать результат после закрытия пула.
:param vk_session: метод
:type vk_session: vk_api.VkAPi
:param method: метод
:type method: str
:param default_values: одинаковые значения для запросов
:type default_values: dict
:param key: ключ изменяющегося параметра
:type key: str
:param values: список значений изменяющегося параметра (max: 25)
:type values: list
:rtype: (dict, dict)
"""

result = {}
errors = {}

for i in range(0, len(values), 25):
current_values = values[i:i + 25]

response_raw = vk_one_param(
vk_session, method, current_values, default_values, key
)

response = response_raw['response']
response_errors = response_raw.get('execute_errors', [])
response_errors_iter = iter(response_errors)

for x, r in enumerate(response):
if r is not False:
result[current_values[x]] = r
else:
errors[current_values[x]] = next(response_errors_iter)

return result, errors


vk_one_param = VkFunction(
args=('method', 'values', 'default_values', 'key'),
clean_args=('method', 'key'),
Expand All @@ -262,14 +254,3 @@ def check_one_method(pool):
return result;
''')


def vk_many_methods(vk_session, pool):
requests = ','.join(
'API.{}({})'.format(i.method, sjson_dumps(i.values))
for i in pool
)

code = 'return [{}];'.format(requests)

return vk_session.method('execute', {'code': code}, raw=True)

0 comments on commit a9e425c

Please sign in to comment.