Source code for vk_api.tools

# -*- coding: utf-8 -*-
"""
:authors: python273
:license: Apache License, Version 2.0, see LICENSE file

:copyright: (c) 2019 python273
"""

from .exceptions import ApiError, VkToolsException
from .execute import VkFunction


[docs]class VkTools(object): """ Содержит некоторые вспомогательные функции, которые могут понадобиться при использовании API :param vk: Объект :class:`VkApi` """ __slots__ = ('vk',) def __init__(self, vk): self.vk = vk
[docs] def get_all_iter(self, method, max_count, values=None, key='items', limit=None, stop_fn=None, negative_offset=False): """ Получить все элементы. Работает в методах, где в ответе есть count и items или users. За один запрос получает max_count * 25 элементов :param method: имя метода :type method: str :param max_count: максимальное количество элементов, которое можно получить за один запрос :type max_count: int :param values: параметры :type values: dict :param key: ключ элементов, которые нужно получить :type key: str :param limit: ограничение на количество получаемых элементов, но может прийти больше :type limit: int :param stop_fn: функция, отвечающая за выход из цикла :type stop_fn: func :param negative_offset: True если offset должен быть отрицательный :type negative_offset: bool """ values = values.copy() if values else {} values['count'] = max_count offset = max_count if negative_offset else 0 items_count = 0 count = None while True: response = vk_get_all_items( self.vk, method, key, values, count, offset, offset_mul=-1 if negative_offset else 1 ) if 'execute_errors' in response: raise VkToolsException( 'Could not load items: {}'.format( response['execute_errors'] ), response=response ) response = response['response'] items = response["items"] items_count += len(items) for item in items: yield item if not response['more']: break if limit and items_count >= limit: break if stop_fn and stop_fn(items): break count = response['count'] offset = response['offset']
[docs] def get_all(self, method, max_count, values=None, key='items', limit=None, stop_fn=None, negative_offset=False): """ Использовать только если нужно загрузить все объекты в память. Eсли вы можете обрабатывать объекты по частям, то лучше использовать get_all_iter Например если вы записываете объекты в БД, то нет смысла загружать все данные в память """ items = list( self.get_all_iter( method, max_count, values, key, limit, stop_fn, negative_offset ) ) return {'count': len(items), key: items}
[docs] def get_all_slow_iter(self, method, max_count, values=None, key='items', limit=None, stop_fn=None, negative_offset=False): """ Получить все элементы (без использования execute) Работает в методах, где в ответе есть count и items или users :param method: имя метода :type method: str :param max_count: максимальное количество элементов, которое можно получить за один запрос :type max_count: int :param values: параметры :type values: dict :param key: ключ элементов, которые нужно получить :type key: str :param limit: ограничение на количество получаемых элементов, но может прийти больше :type limit: int :param stop_fn: функция, отвечающая за выход из цикла :type stop_fn: func :param negative_offset: True если offset должен быть отрицательный :type negative_offset: bool """ values = values.copy() if values else {} values['count'] = max_count offset_mul = -1 if negative_offset else 1 offset = max_count if negative_offset else 0 count = None items_count = 0 while count is None or offset < count: values['offset'] = offset * offset_mul response = self.vk.method(method, values) new_count = response['count'] count_diff = (new_count - count) if count is not None else 0 if count_diff < 0: offset += count_diff count = new_count continue response_items = response[key] items = response_items[count_diff:] items_count += len(items) for item in items: yield item if len(response_items) < max_count - count_diff: break if limit and items_count >= limit: break if stop_fn and stop_fn(items): break offset += max_count count = new_count
[docs] def get_all_slow(self, method, max_count, values=None, key='items', limit=None, stop_fn=None, negative_offset=False): """ Использовать только если нужно загрузить все объекты в память. Eсли вы можете обрабатывать объекты по частям, то лучше использовать get_all_slow_iter Например если вы записываете объекты в БД, то нет смысла загружать все данные в память """ items = list( self.get_all_slow_iter( method, max_count, values, key, limit, stop_fn, negative_offset ) ) return {'count': len(items), key: items}
vk_get_all_items = VkFunction( args=('method', 'key', 'values', 'count', 'offset', 'offset_mul'), clean_args=('method', 'key', 'offset', 'offset_mul'), return_raw=True, code=''' var params = %(values)s, calls = 0, items = [], count = %(count)s, offset = %(offset)s, ri; while(calls < 25) { calls = calls + 1; params.offset = offset * %(offset_mul)s; var response = API.%(method)s(params), new_count = response.count, count_diff = (count == null ? 0 : new_count - count); if (!response) { return {"_error": 1}; } if (count_diff < 0) { offset = offset + count_diff; } else { ri = response.%(key)s; items = items + ri.slice(count_diff); offset = offset + params.count + count_diff; if (ri.length < params.count) { calls = 99; } } count = new_count; if (count != null && offset >= count) { calls = 99; } }; return { count: count, items: items, offset: offset, more: calls != 99 }; ''')