8000 Merge pull request #904 from jeblair/timeout · OneShed/github3.py@b8f81a0 · GitHub
[go: up one dir, main page]

Skip to content

Commit b8f81a0

Browse files
authored
Merge pull request sigmavirus24#904 from jeblair/timeout
Set timeout on HTTP requests
2 parents b5d51ce + 3247137 commit b8f81a0

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

AUTHORS.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,5 @@ Contributors
174174
- Raphael Vogel (@RaphaelVogel)
175175

176176
- Andreas Burger (@AndreasBurger)
177+
178+
- James E. Blair (@jeblair)

docs/source/api-reference/github.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@ GitHubStatus Object
3434

3535
.. autoclass:: github3.github.GitHubStatus
3636
:members:
37+
38+
39+
GitHubSession Object
40+
====================
41+
42+
.. autoclass:: github3.session.GitHubSession

src/github3/session.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,40 @@ def __call__(self, request):
6464

6565

6666
class GitHubSession(requests.Session):
67-
"""Our slightly specialized Session object."""
67+
"""Our slightly specialized Session object.
68+
69+
Normally this is created automatically by
70+
:class:`~github3.github.GitHub`. To use alternate values for
71+
network timeouts, this class can be instantiated directly and
72+
passed to the GitHub object. For example:
73+
74+
.. code-block:: python
75+
76+
gh = github.GitHub(session=session.GitHubSession(
77+
default_connect_timeout=T, default_read_timeout=N))
78+
79+
:param default_connect_timeout:
80+
the number of seconds to wait when establishing a connection to
81+
GitHub
82+
:type default_connect_timeout:
83+
float
84+
:param default_read_timeout:
85+
the number of seconds to wait for a response from GitHub
86+
:type default_read_timeout:
87+
float
88+
"""
6889

6990
auth = None
7091
__attrs__ = requests.Session.__attrs__ + [
7192
"base_url",
7293
"two_factor_auth_cb",
7394
]
7495

75-
def __init__(self):
96+
def __init__(self, default_connect_timeout=4, default_read_timeout=1):
7697
"""Slightly modify how we initialize our session."""
7798
super(GitHubSession, self).__init__()
99+
self.default_connect_timeout = default_connect_timeout
100+
self.default_read_timeout = default_read_timeout
78101
self.headers.update(
79102
{
80103
# Only accept JSON responses
@@ -91,6 +114,11 @@ def __init__(self):
91114
self.two_factor_auth_cb = None
92115
self.request_counter = 0
93116

117+
@property
118+
def timeout(self):
119+
"""Return the timeout tuple as expected by Requests"""
120+
return (self.default_connect_timeout, self.default_read_timeout)
121+
94122
def basic_auth(self, username, password):
95123
"""Set the Basic Auth credentials on this Session.
96124
@@ -137,6 +165,7 @@ def oauth2_auth(self, client_id, client_secret):
137165

138166
def request(self, *args, **kwargs):
139167
"""Make a request, count it, and handle 2FA if necessary."""
168+
kwargs.setdefault('timeout', self.timeout)
140169
response = super(GitHubSession, self).request(*args, **kwargs)
141170
self.request_counter += 1
142171
if requires_2fa(response) and self.two_factor_auth_cb:

tests/unit/test_github_session.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,37 @@ def test_has_default_headers(self):
3030
assert "User-Agent" in s.headers
3131
assert s.headers["User-Agent"].startswith("github3.py/")
3232

33+
@mock.patch.object(requests.Session, 'request')
34+
def test_default_timeout(self, request_mock):
35+
"""Test that default timeout values are used"""
36+
response = mock.Mock()
37+
response.configure_mock(status_code=200, headers={})
38+
request_mock.return_value = response
39+
s = self.build_session()
40+
r = s.get("http://example.com")
41+
assert r is response
42+
request_mock.assert_called_once_with(
43+
"GET", "http://example.com", allow_redirects=True,
44+
timeout=(4, 1)
45+
)
46+
47+
@mock.patch.object(requests.Session, 'request')
48+
def test_custom_timeout(self, request_mock):
49+
"""Test that custom timeout values are used"""
50+
response = mock.Mock()
51+
response.configure_mock(status_code=200, headers={})
52+
request_mock.return_value = response
53+
s = session.GitHubSession(
54+
default_connect_timeout=300,
55+
default_read_timeout=400
56+
)
57+
r = s.get("http://example.com")
58+
assert r is response
59+
request_mock.assert_called_once_with(
60+
"GET", "http://example.com", allow_redirects=True,
61+
timeout=(300, 400)
62+
)
63+
3364
def test_build_url(self):
3465
"""Test that GitHubSessions build basic URLs"""
3566
s = self.build_session()
@@ -117,7 +148,8 @@ def test_request_ignores_responses_that_do_not_require_2fa(
117148
r = s.get("http://example.com")
118149
assert r is response
119150
request_mock.assert_called_once_with(
120-
"GET", "http://example.com", allow_redirects=True
151+
"GET", "http://example.com", allow_redirects=True,
152+
timeout=(4, 1)
121153
)
122154

123155
@mock.patch.object(requests.Session, "request")

0 commit comments

Comments
 (0)
0