Skip to content
This repository was archived by the owner on May 7, 2024. It is now read-only.

Commit 28e4244

Browse files
committed
Add global_request_timeout and max_request_retries configuration options. Set default request timeout to 5s. Add basic tests instantiating Cloudflare.Cloudflare
fixes #49
1 parent 0ea52bc commit 28e4244

File tree

8 files changed

+136
-36
lines changed

8 files changed

+136
-36
lines changed

CloudFlare/cloudflare.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ def __init__(self, config):
3838

3939
self.raw = config['raw']
4040
self.use_sessions = config['use_sessions']
41+
self.global_request_timeout = config['global_request_timeout'] if 'global_request_timeout' in config else None
42+
self.max_request_retries = config['max_request_retries'] if 'max_request_retries' in config else None
4143
self.profile = config['profile']
42-
self.network = CFnetwork(use_sessions=self.use_sessions)
44+
self.network = CFnetwork(
45+
use_sessions=self.use_sessions,
46+
global_request_timeout=self.global_request_timeout,
47+
max_request_retries=self.max_request_retries
48+
)
4349
self.user_agent = user_agent()
4450

4551
self.logger = CFlogger(config['debug']).getLogger() if 'debug' in config and config['debug'] else None
@@ -909,7 +915,7 @@ def api_from_openapi(self, url):
909915

910916
return api_decode_from_openapi(self._base.api_from_openapi(url))
911917

912-
def __init__(self, email=None, key=None, token=None, certtoken=None, debug=False, raw=False, use_sessions=True, profile=None, base_url=None):
918+
def __init__(self, email=None, key=None, token=None, certtoken=None, debug=False, raw=False, use_sessions=True, profile=None, base_url=None, global_request_timeout=5, max_request_retries=5):
913919
""" Cloudflare v4 API"""
914920

915921
self._base = None
@@ -938,6 +944,10 @@ def __init__(self, email=None, key=None, token=None, certtoken=None, debug=False
938944
config['profile'] = profile
939945
if base_url is not None:
940946
config['base_url'] = base_url
947+
if global_request_timeout is not None:
948+
config['global_request_timeout'] = global_request_timeout
949+
if max_request_retries is not None:
950+
config['max_request_retries'] = max_request_retries
941951

942952
# we do not need to handle item.call values - they pass straight thru
943953

CloudFlare/network.py

+89-16
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,133 @@
11
""" Network for Cloudflare API"""
22
from __future__ import absolute_import
33

4+
from urllib.parse import urlparse
5+
46
import requests
7+
from requests.adapters import HTTPAdapter
58

69
from .exceptions import CloudFlareAPIError
710

8-
class CFnetwork():
9-
""" Network for Cloudflare API"""
1011

11-
def __init__(self, use_sessions=True):
12-
""" Network for Cloudflare API"""
12+
class CFnetwork:
13+
"""Network for Cloudflare API"""
14+
15+
def __init__(
16+
self, max_request_retries, use_sessions=True, global_request_timeout=5,
17+
):
18+
"""Network for Cloudflare API"""
1319

1420
self.use_sessions = use_sessions
21+
self.global_request_timeout = global_request_timeout
22+
self.max_request_retries = max_request_retries
1523
self.session = None
1624

1725
def __call__(self, method, url, headers=None, params=None, data=None, files=None):
18-
""" Network for Cloudflare API"""
26+
"""Network for Cloudflare API"""
1927

2028
if self.use_sessions:
2129
if self.session is None:
22-
self.session = requests.Session()
30+
s = requests.Session()
31+
if self.max_request_retries is not None:
32+
hostname = urlparse(url).netloc
33+
s.mount(
34+
f"https://{hostname}",
35+
HTTPAdapter(max_retries=self.max_request_retries),
36+
)
37+
self.session = s
2338
else:
2439
self.session = requests
2540

2641
method = method.upper()
2742

2843
if method == 'GET':
29-
r = self.session.get(url, headers=headers, params=params, data=data)
44+
r = self.session.get(
45+
url,
46+
headers=headers,
47+
params=params,
48+
data=data,
49+
timeout=self.global_request_timeout,
50+
)
3051
elif method == 'POST':
3152
if isinstance(data, str):
32-
r = self.session.post(url, headers=headers, params=params, data=data, files=files)
53+
r = self.session.post(
54+
url,
55+
headers=headers,
56+
params=params,
57+
data=data,
58+
files=files,
59+
timeout=self.global_request_timeout,
60+
)
3361
else:
34-
r = self.session.post(url, headers=headers, params=params, json=data, files=files)
62+
r = self.session.post(
63+
url,
64+
headers=headers,
65+
params=params,
66+
json=data,
67+
files=files,
68+
timeout=self.global_request_timeout,
69+
)
3570
elif method == 'PUT':
3671
if isinstance(data, str):
37-
r = self.session.put(url, headers=headers, params=params, data=data)
72+
r = self.session.put(
73+
url,
74+
headers=headers,
75+
params=params,
76+
data=data,
77+
timeout=self.global_request_timeout,
78+
)
3879
else:
39-
r = self.session.put(url, headers=headers, params=params, json=data)
80+
r = self.session.put(
81+
url,
82+
headers=headers,
83+
params=params,
84+
json=data,
85+
timeout=self.global_request_timeout,
86+
)
4087
elif method == 'DELETE':
4188
if isinstance(data, str):
42-
r = self.session.delete(url, headers=headers, params=params, data=data)
89+
r = self.session.delete(
90+
url,
91+
headers=headers,
92+
params=params,
93+
data=data,
94+
timeout=self.global_request_timeout,
95+
)
4396
else:
44-
r = self.session.delete(url, headers=headers, params=params, json=data)
97+
r = self.session.delete(
98+
url,
99+
headers=headers,
100+
params=params,
101+
json=data,
102+
timeout=self.global_request_timeout,
103+
)
45104
elif method == 'PATCH':
46105
if isinstance(data, str):
47-
r = self.session.request('PATCH', url, headers=headers, params=params, data=data)
106+
r = self.session.request(
107+
'PATCH',
108+
url,
109+
headers=headers,
110+
params=params,
111+
data=data,
112+
timeout=self.global_request_timeout,
113+
)
48114
else:
49-
r = self.session.request('PATCH', url, headers=headers, params=params, json=data)
115+
r = self.session.request(
116+
'PATCH',
117+
url,
118+
headers=headers,
119+
params=params,
120+
json=data,
121+
timeout=self.global_request_timeout,
122+
)
50123
else:
51124
# should never happen
52125
raise CloudFlareAPIError(0, 'method not supported')
53126

54127
return r
55128

56129
def __del__(self):
57-
""" Network for Cloudflare API"""
130+
"""Network for Cloudflare API"""
58131

59132
if self.use_sessions and self.session:
60133
self.session.close()

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PYTHON = python
33
# PANDOC = pandoc
44
PYLINT = pylint
55
TWINE = twine
6+
PYTEST = pytest
67

78
89
NAME = "cloudflare"
@@ -36,7 +37,7 @@ install: build
3637
sudo rm -rf ${NAME}.egg-info
3738

3839
test: all
39-
# to be done
40+
$(PYTEST) -vv
4041

4142
cli4test: all
4243
$(PYTHON) -m cli4 /ips > /dev/null

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,12 @@ When you create a **CloudFlare** class you can pass some combination of these fo
180180

181181
This parameter controls how the data is returned from a successful call (see notes below).
182182

183-
* `raw - An optional Raw flag (True/False) - defaults to False
183+
* `raw` - An optional Raw flag (True/False) - defaults to False
184+
185+
Timeouts (10s) and Retries (5) are configured by default. Should you wish to override them, use these settings:
186+
* `global_request_timeout` - How long before each API call to Cloudflare should time out (in seconds)
187+
* `max_requests_retries` - How many times to retry an API call when DNS lookups, socket connections, or connect timeouts occur.
188+
> NOTE: `max_request_retries` is only available when `use_sessions` is not disabled.
184189
185190
The following paramaters are for debug and/or development usage
186191

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ future
33
pyyaml
44
jsonlines
55
beautifulsoup4
6+
pytest

setup.cfg

+7
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@
99
#
1010
#[upload]
1111
#
12+
[options.extras_require]
13+
test =
14+
pytest
15+
16+
[tool:pytest]
17+
testpaths =
18+
tests

tests/test1.py

-16
This file was deleted.

tests/test_cloudflare.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
import sys
3+
sys.path.insert(0, os.path.abspath('..'))
4+
5+
import CloudFlare
6+
7+
class TestCloudflare:
8+
def test_creating_default_client(self):
9+
cf = CloudFlare.CloudFlare()
10+
assert isinstance(cf, CloudFlare.CloudFlare)
11+
12+
13+
def test_with_global_request_timeout(self):
14+
cf = CloudFlare.CloudFlare({'global_request_timeout': 10})
15+
assert isinstance(cf, CloudFlare.CloudFlare)
16+
17+
def test_with_max_request_retries(self):
18+
cf = CloudFlare.CloudFlare({'max_request_retries': 2})
19+
assert isinstance(cf, CloudFlare.CloudFlare)

0 commit comments

Comments
 (0)