Skip to content

Commit 6ff0783

Browse files
committed
Persistent sessions between API-calls
1 parent b9c9b4f commit 6ff0783

File tree

7 files changed

+85
-39
lines changed

7 files changed

+85
-39
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Changelog
22

3+
## v1.0.39 (2020-09-22)
4+
* Persistent sessions between API-calls
35

46
## v1.0.38 (2020-09-04)
57
* Text-Analytics API additional functions: `get_analytics` and `get_annotated`

ravenpackapi/core.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import logging
22
import os
33

4-
import requests
5-
64
from ravenpackapi import Dataset
75
from ravenpackapi.exceptions import APIException
86
from ravenpackapi.models.dataset_list import DatasetList
@@ -13,9 +11,10 @@
1311
from ravenpackapi.util import to_curl
1412
from ravenpackapi.utils.constants import ENTITY_TYPES
1513
from ravenpackapi.utils.date_formats import as_datetime_str
14+
from ravenpackapi.utils.dynamic_sessions import DynamicSession
1615

1716
_VALID_METHODS = ('get', 'post', 'put', 'delete', 'patch')
18-
VERSION = '1.0.38'
17+
VERSION = '1.0.39'
1918

2019
logger = logging.getLogger("ravenpack.core")
2120

@@ -45,6 +44,7 @@ def __init__(self, api_key=None):
4544
)
4645
self.api_key = api_key
4746
self.log_curl_commands = True
47+
self.session = DynamicSession()
4848
self.upload = UploadApi(self)
4949

5050
@property
@@ -62,7 +62,7 @@ def request(self, endpoint, data=None, json=None,
6262
'Method {used} not accepted. Please choose one of {valid_methods}'.format(
6363
used=method, valid_methods=", ".join(_VALID_METHODS)
6464
)
65-
requests_call = getattr(requests, method)
65+
requests_call = getattr(self.session, method)
6666
logger.debug("Request {method} to {endpoint}".format(method=method,
6767
endpoint=endpoint))
6868
if endpoint.startswith("http://") or endpoint.startswith("https://"):

ravenpackapi/models/dataset.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,11 @@ def request_realtime(self):
298298
dataset_id=self.id)
299299
logger.debug("Connecting with RT feed: %s" % endpoint)
300300
try:
301-
response = requests.get(endpoint,
302-
headers=api.headers,
303-
stream=True,
304-
**api.common_request_params
305-
)
301+
response = api.session.get(endpoint,
302+
headers=api.headers,
303+
stream=True,
304+
**api.common_request_params
305+
)
306306
if response.status_code != 200:
307307
logger.error("Error calling the API, we tried: %s" % to_curl(response.request))
308308
raise APIException(

ravenpackapi/models/job.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import logging
33
from time import sleep
44

5-
import requests
6-
75
from ravenpackapi.exceptions import (api_method,
86
APIException,
97
DataFileTimeout,
@@ -112,11 +110,11 @@ def save_to_file(self, filename):
112110

113111
# this is a different request than the normal API
114112
# streaming the file in chunks
115-
response = requests.get(job.url,
116-
headers=api.headers,
117-
stream=True,
118-
**api.common_request_params
119-
)
113+
response = api.session.get(job.url,
114+
headers=api.headers,
115+
stream=True,
116+
**api.common_request_params
117+
)
120118
if response.status_code != 200:
121119
logger.error("Error calling the API, we tried: %s" % to_curl(response.request))
122120
raise APIException(
@@ -133,22 +131,21 @@ def iterate_results(self, include_headers=False):
133131
job = self # just to be clear
134132
job.wait_for_completion()
135133

136-
with requests.Session() as s:
137-
r = s.get(job.url,
138-
headers=api.headers,
139-
stream=True,
140-
**api.common_request_params
141-
)
142-
iterator = r.iter_lines(chunk_size=self._CHUNK_SIZE)
134+
r = api.session.get(job.url,
135+
headers=api.headers,
136+
stream=True,
137+
**api.common_request_params
138+
)
139+
iterator = r.iter_lines(chunk_size=self._CHUNK_SIZE)
143140

144-
headers = next(iterator) # discard the headers
141+
headers = next(iterator) # discard the headers
145142

146-
if include_headers:
147-
yield parse_csv_line(headers)
143+
if include_headers:
144+
yield parse_csv_line(headers)
148145

149-
for line in iterator:
150-
fields = parse_csv_line(line)
151-
yield fields
146+
for line in iterator:
147+
fields = parse_csv_line(line)
148+
yield fields
152149

153150
def __iter__(self):
154151
# this will be yield from in Py3

ravenpackapi/tests/test_job_cancellation.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ def test_job_cancel(self):
3737
job.cancel()
3838
except APIException as exception:
3939
# cancel raised an exception, means that we were already processing it
40-
assert status == 'processing'
41-
assert exception.response.status_code == 400
42-
else:
43-
assert status == 'enqueued'
44-
assert job.get_status() == 'cancelled'
45-
46-
assert job.is_processing is False
47-
with pytest.raises(JobNotProcessing):
48-
job.wait_for_completion()
40+
if status == 'processing':
41+
assert exception.response.status_code == 400
42+
else:
43+
assert status == 'enqueued'
44+
assert job.get_status() == 'cancelled'
45+
46+
assert job.is_processing is False
47+
with pytest.raises(JobNotProcessing):
48+
job.wait_for_completion()
4949

5050
@classmethod
5151
def teardown_class(cls):
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from collections import defaultdict
2+
from functools import partial
3+
4+
import requests
5+
from six.moves.urllib.parse import urlparse
6+
7+
_REQUESTS_METHODS = ('get', 'post', 'put', 'delete', 'patch')
8+
9+
10+
# see test_dynamic_sessions.py for how to use it
11+
12+
class DynamicSession(object):
13+
""" This looks intricate but its a way to make requests.session work transparently creating different
14+
session depending on the hostname of the urls
15+
"""
16+
_session_by_host = defaultdict(requests.session)
17+
18+
@staticmethod
19+
def get_session_from_args(args, kwargs):
20+
url = kwargs.get('url', None)
21+
if url is None:
22+
url = args[0] # get the positional url
23+
parsed_uri = urlparse(url)
24+
session_key = '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri)
25+
if 'verify' in kwargs:
26+
session_key += "-noverify" # unverified calls get a different session
27+
session = DynamicSession._session_by_host[session_key]
28+
return session
29+
30+
@staticmethod
31+
def get_session_method(method, *args, **kwargs):
32+
# this will be returned as a partial - it will get a session for the url in the call
33+
# that will get the proper session for that host
34+
session = DynamicSession.get_session_from_args(args, kwargs)
35+
return getattr(session, method)(*args, **kwargs)
36+
37+
@staticmethod
38+
def __getattr__(method):
39+
# when we do DynamicSession.post - it will return a partial - that will ask to get
40+
if method in _REQUESTS_METHODS:
41+
return partial(DynamicSession.get_session_method, method)
42+
return getattr(requests, method)
43+
44+
@staticmethod
45+
def get_session(*args, **kwargs):
46+
""" This is the only method that is only from here - everything else is taken from requests """
47+
return DynamicSession.get_session_from_args(args, kwargs)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from setuptools import setup, find_packages
22

3-
VERSION = '1.0.38'
3+
VERSION = '1.0.39'
44

55
with open('README.rst') as readme_file:
66
readme = readme_file.read()

0 commit comments

Comments
 (0)