Skip to content

Commit

Permalink
Merge branch 'release/1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
prawn-cake committed Oct 18, 2017
2 parents 65cfd22 + 8b2cb7f commit af61880
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.9.9
current_version = 1.0.0
tag = False
tag_name = {new_version}
commit = True
Expand Down
6 changes: 5 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
Changelog
=========

0.9.9 (2017-07-18)
1.0.0 (2017-10-18)
--------------------
* [Feature] #26: Service token support

0.9.9 (2017-07-18)
------------------
* [Bugfix] #25: Fix logging issues

0.9.8 (2017-04-01)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ clean:
.PHONY: env
env:
# target: env - create virtualenv and install packages
@virtualenv $(VIRTUAL_ENV)
@virtualenv --python=python3 $(VIRTUAL_ENV)
@$(VIRTUAL_ENV)/bin/pip install -r $(CURDIR)/requirements-test.txt


Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,41 @@
========================================================================================================
[![Build Status](https://travis-ci.org/prawn-cake/vk-requests.svg?branch=master)](https://travis-ci.org/prawn-cake/vk-requests)
[![Coverage Status](https://coveralls.io/repos/prawn-cake/vk-requests/badge.svg?branch=master&service=github)](https://coveralls.io/github/prawn-cake/vk-requests?branch=master)
![PythonVersions](https://www.dropbox.com/s/ck0nc28ttga2pw9/python-2.7_3.4-blue.svg?dl=1)
[![GitHub issues](https://img.shields.io/github/issues/prawn-cake/vk-requests.svg)](https://github.com/prawn-cake/vk-requests/issues)

[vk.com](https://vk.com) is the largest social network in Russia.
This library is significantly improved fork of [vk](https://github.com/dimka665/vk)

## Requirements

* python (2.7, 3.4, 3.5, 3.6)


## Install

pip install vk-requests

## Usage

#### Using user token

import vk_requests


api = vk_requests.create_api(app_id=123, login='User', password='Password')
api.users.get(user_ids=1)
[{'first_name': 'Pavel', 'last_name': 'Durov', 'id': 1}]

#### Using service token

api = vk_requests.create_api(service_token="{YOUR_APP_SERVICE_TOKEN}")
...

**NOTE:** service token is preferable way, because it does not require user
credentials and oauth requests, but **not all the methods can be called with service token** (e.g *execute* can't be)

More info about service token: [Link](https://vk.com/dev/service_token)

### Custom scope or api version requests

Just pass `scope` and/or `api_version` parameters like
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

setup(
name='vk-requests',
version='0.9.9',
version='1.0.0',
packages=['vk_requests'],
url='https://github.com/prawn-cake/vk-requests',
license='MIT',
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27,py34,py35
envlist = py27,py34,py35,py36

[testenv]
passenv = *
Expand Down
8 changes: 5 additions & 3 deletions vk_requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from vk_requests.api import API


__version__ = '0.9.9'
__version__ = '1.0.0'


def create_api(app_id=None, login=None, password=None, phone_number=None,
timeout=10, scope='offline', api_version=None,
interactive=False, **method_default_args):
interactive=False, service_token=None, **method_default_args):
"""Factory method to explicitly create API with app_id, login, password
and phone_number parameters.
Expand All @@ -23,16 +23,18 @@ def create_api(app_id=None, login=None, password=None, phone_number=None,
:param scope: str or list of str: vk session scope
:param api_version: str: vk api version, check https://vk.com/dev/versions
:param interactive: bool: flag which indicates to use InteractiveVKSession
:param service_token: str: new way of querying vk api, instead of getting
oauth token
:param method_default_args: api kwargs
:return: api instance
:rtype : vk_requests.api.API
"""

session = VKSession(app_id=app_id,
user_login=login,
user_password=password,
phone_number=phone_number,
scope=scope,
service_token=service_token,
api_version=api_version,
interactive=interactive)
return API(session=session, timeout=timeout, **method_default_args)
Expand Down
32 changes: 15 additions & 17 deletions vk_requests/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,36 @@


class API(object):
def __init__(self, session, timeout=10, **default_request_kwargs):
self._session = session
self._timeout = timeout
self._default_request_kwargs = default_request_kwargs

def get_default_kwargs(self):
"""Getter method.
It is used in vk_requests.session.VKSession.send_api_request
def __init__(self, session, http_params=None, **default_request_kwargs):
"""
:return: copy of default requests kwargs dict
:param session: vk_requests.session.VKSession instance
:param http_params: dict: requests HTTP parameters
:param default_request_kwargs:
"""
return self._default_request_kwargs.copy()
self._session = session
if http_params is None:
self.http_params = dict(timeout=10)
self._default_request_kwargs = default_request_kwargs

def __getattr__(self, method_name):
return Request(session=self._session,
method_name=method_name,
timeout=self._timeout)
http_params=self.http_params)


class Request(object):
__slots__ = ('_session', 'timeout', '_method_name', '_method_args')
__slots__ = ('_session', 'http_params', '_method_name', '_method_args')

def __init__(self, session, method_name, timeout=10):
def __init__(self, session, method_name, http_params):
"""
:param session: vk_requests.session.VKSession instance
:param session: vk_requests.session.VKSession instance
:param method_name: str: method name
"""
self._session = session
self._method_name = method_name
self._method_args = None # will be set with __call__ execution
self.timeout = timeout
self.http_params = http_params

@property
def method_name(self):
Expand All @@ -59,7 +57,7 @@ def __getattr__(self, method_name):
new_method = '.'.join([self._method_name, method_name])
return Request(session=self._session,
method_name=new_method,
timeout=self.timeout)
http_params=self.http_params)

def __call__(self, **method_args):
self._method_args = method_args
Expand Down
52 changes: 35 additions & 17 deletions vk_requests/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ class VKSession(object):

def __init__(self, app_id=None, user_login=None, user_password=None,
phone_number=None, scope='offline', api_version=None,
interactive=False, **api_kwargs):
interactive=False, service_token=None):
"""IMPORTANT: (app_id + user_login + user_password) and service_token
are mutually exclusive
"""
self.app_id = app_id
self._login = user_login
self._password = user_password
self._api_kwargs = api_kwargs
self._phone_number = phone_number
self._service_token = service_token
self.scope = scope
self.interactive = interactive
self._access_token = None
Expand Down Expand Up @@ -117,18 +120,19 @@ def do_login(self, http_session):
session_cookies = ('remixsid' in http_session.cookies,
'remixsid6' in http_session.cookies)
if any(session_cookies):
logger.info('Session is already established')
return None
logger.info('VK session is established')
return True
else:
message = 'Authorization error: incorrect password or ' \
'authentication code'
logger.error(message)
raise VkAuthError(message)

def do_oauth2_authorization(self, session):
""" OAuth2. More info: https://vk.com/dev/auth_mobile
""" OAuth2 authorization method. It's used for getting access token
More info: https://vk.com/dev/auth_mobile
"""
logger.info('Doing oauth2')
logger.info('Doing oauth2, app_id=%s', self.app_id)
auth_data = {
'client_id': self.app_id,
'display': 'mobile',
Expand Down Expand Up @@ -215,7 +219,7 @@ def require_phone_number(self, html, session):

# Raises VkPageWarningsError in case of warnings
# NOTE: we check only 'security_check' case on warnings for now
# in future it might be propagated to other cases as well
# in future it might be extended for other cases as well
check_html_warnings(html=html)

# Determine form action url
Expand All @@ -227,9 +231,14 @@ def require_phone_number(self, html, session):
if self._phone_number:
code = self._phone_number[len(phone_prefix):-len(phone_suffix)]
else:
prompt = 'Enter missing digits of your phone number (%s****%s): '\
% (phone_prefix, phone_suffix)
code = raw_input(prompt)
if self.interactive:
prompt = 'Enter missing digits of your phone number (%s****%s): '\
% (phone_prefix, phone_suffix)
code = raw_input(prompt)
else:
raise VkAuthError(
'Phone number is required. Create an API instance using '
'phone_number parameter or use interactive mode')

params = parse_url_query_params(action_url, fragment=False)
auth_data = {
Expand Down Expand Up @@ -257,8 +266,13 @@ def access_token(self):
return self._access_token

def _get_access_token(self):
"""Get access token using app_id, login and password.
"""Get access token using app_id, login and password OR service token
(service token docs: https://vk.com/dev/service_token
"""
if self._service_token:
logger.info('Use service token: %s',
5 * '*' + self._service_token[50:])
return self._service_token

if not all([self.app_id, self._login, self._password]):
raise ValueError(
Expand All @@ -267,13 +281,13 @@ def _get_access_token(self):
'*' * len(self._password) if self._password else 'None'))

logger.info("Getting access token for user '%s'" % self._login)
with VerboseHTTPSession() as s:
with self.http_session as s:
self.do_login(http_session=s)
url_query_params = self.do_oauth2_authorization(session=s)
logger.debug('url_query_params: %s', url_query_params)

if 'access_token' in url_query_params:
logger.info('Done')
logger.info('Access token has been gotten')
return url_query_params['access_token']
else:
raise VkAuthError('OAuth2 authorization error. Url params: %s'
Expand Down Expand Up @@ -303,7 +317,7 @@ def make_request(self, request_obj, captcha_response=None):
:param request_obj: vk_requests.api.Request instance
:param captcha_response: None or dict, e.g {'sid': <sid>, 'key': <key>}
:return:
:return: dict: json decoded http response
"""
logger.debug('Prepare API Method request %r', request_obj)
response = self.send_api_request(request_obj=request_obj,
Expand Down Expand Up @@ -353,18 +367,22 @@ def send_api_request(self, request_obj, captcha_response=None):

# Prepare request arguments
method_kwargs = {'v': self.api_version}

for values in (request_obj.method_args, ):
method_kwargs.update(stringify_values(values))

if self.is_token_required():
if self.is_token_required() or self._service_token:
# Auth api call if access_token hadn't been gotten earlier
method_kwargs['access_token'] = self.access_token

if captcha_response:
method_kwargs['captcha_sid'] = captcha_response['sid']
method_kwargs['captcha_key'] = captcha_response['key']

response = self.http_session.post(
url=url, data=method_kwargs, timeout=request_obj.timeout)
http_params = dict(url=url,
data=method_kwargs,
**request_obj.http_params)
response = self.http_session.post(**http_params)
return response

def __repr__(self): # pragma: no cover
Expand Down
1 change: 1 addition & 0 deletions vk_requests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
# aka API/Client ID
APP_ID = os.getenv('VK_APP_ID')
PHONE_NUMBER = os.getenv('VK_PHONE_NUMBER')
SERVICE_TOKEN = os.getenv('VK_SERVICE_TOKEN')
14 changes: 12 additions & 2 deletions vk_requests/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from vk_requests.exceptions import VkAPIError
from vk_requests import VKSession
from vk_requests.settings import APP_ID, USER_LOGIN, USER_PASSWORD, \
PHONE_NUMBER
PHONE_NUMBER, SERVICE_TOKEN

try:
from unittest import mock
Expand Down Expand Up @@ -146,7 +146,7 @@ def test_execute(self):
def test_set_status(self):
"""Test requires scope='status' vk permissions
"""
status_text = 'Welcome to noagent.168.estate'
status_text = 'Welcome to noagent.estate'
api = self._create_api(scope=['offline', 'status'])
self.assertEqual(api._session.scope, ['offline', 'status'])

Expand Down Expand Up @@ -177,3 +177,13 @@ def test_likes_get_list(self):
self.assertIn('items', resp)
for user_id in resp['items']:
self.assertIsInstance(user_id, int)


class VkApiMethodsCalledWithServiceTokenTest(unittest.TestCase):
def setUp(self):
self.api = vk_requests.create_api(service_token=SERVICE_TOKEN)

def test_wall_get(self):
resp = self.api.wall.get(owner_id=1)
self.assertIn('items', resp)
self.assertIn('count', resp)

0 comments on commit af61880

Please sign in to comment.