-
Notifications
You must be signed in to change notification settings - Fork 426
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try to reuse the HttpClient between requests by default #526
Changes from 7 commits
b928e8e
c90a6ae
1756e79
661b04c
3b86e58
f3627a5
e090fa7
f38bb7e
652ef29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import sys | ||
import socket | ||
from threading import Thread | ||
import json | ||
|
||
import stripe | ||
import pytest | ||
|
||
if sys.version_info[0] < 3: | ||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer | ||
else: | ||
from http.server import BaseHTTPRequestHandler, HTTPServer | ||
|
||
|
||
def get_free_port(): | ||
s = socket.socket(socket.AF_INET, type=socket.SOCK_STREAM) | ||
s.bind(("localhost", 0)) | ||
address, port = s.getsockname() | ||
s.close() | ||
return port | ||
|
||
|
||
class TestIntegration(object): | ||
@pytest.fixture(autouse=True) | ||
def close_mock_server(self): | ||
yield | ||
if self.mock_server: | ||
self.mock_server.shutdown() | ||
self.mock_server_thread.join() | ||
|
||
@pytest.fixture(autouse=True) | ||
def setup_stripe(self): | ||
orig_attrs = { | ||
"api_base": stripe.api_base, | ||
"api_key": stripe.api_key, | ||
"default_http_client": stripe.default_http_client, | ||
"log": stripe.log, | ||
"proxy": stripe.proxy, | ||
} | ||
stripe.api_base = "http://localhost:12111" # stripe-mock | ||
stripe.api_key = "sk_test_123" | ||
stripe.default_http_client = None | ||
stripe.log = "warning" | ||
stripe.proxy = None | ||
yield | ||
stripe.api_base = orig_attrs["api_base"] | ||
stripe.api_key = orig_attrs["api_key"] | ||
stripe.default_http_client = orig_attrs["default_http_client"] | ||
stripe.log = orig_attrs["log"] | ||
stripe.proxy = orig_attrs["proxy"] | ||
|
||
def setup_mock_server(self, handler): | ||
# Configure mock server. | ||
self.mock_server_port = get_free_port() | ||
self.mock_server = HTTPServer( | ||
("localhost", self.mock_server_port), handler | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeesh, I was surprised to find that Python's docs for this are astoundingly awful, but passing Here's an example with if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print "Server loop running in thread:", server_thread.name
client(ip, port, "Hello World 1")
client(ip, port, "Hello World 2")
client(ip, port, "Hello World 3")
server.shutdown()
server.server_close() |
||
|
||
# Start running mock server in a separate thread. | ||
# Daemon threads automatically shut down when the main process exits. | ||
self.mock_server_thread = Thread(target=self.mock_server.serve_forever) | ||
self.mock_server_thread.setDaemon(True) | ||
self.mock_server_thread.start() | ||
|
||
def test_hits_api_base(self): | ||
class MockServerRequestHandler(BaseHTTPRequestHandler): | ||
num_requests = 0 | ||
|
||
def do_GET(self): | ||
self.__class__.num_requests += 1 | ||
|
||
self.send_response(200) | ||
self.send_header( | ||
"Content-Type", "application/json; charset=utf-8" | ||
) | ||
self.end_headers() | ||
self.wfile.write(json.dumps({}).encode("utf-8")) | ||
return | ||
|
||
self.setup_mock_server(MockServerRequestHandler) | ||
|
||
stripe.api_base = "http://localhost:%s" % self.mock_server_port | ||
stripe.Balance.retrieve() | ||
assert MockServerRequestHandler.num_requests == 1 | ||
|
||
def test_hits_stripe_proxy(self, mocker): | ||
class MockServerRequestHandler(BaseHTTPRequestHandler): | ||
num_requests = 0 | ||
|
||
def do_GET(self): | ||
self.__class__.num_requests += 1 | ||
|
||
self.send_response(200) | ||
self.send_header( | ||
"Content-Type", "application/json; charset=utf-8" | ||
) | ||
self.end_headers() | ||
self.wfile.write(json.dumps({}).encode("utf-8")) | ||
return | ||
|
||
self.setup_mock_server(MockServerRequestHandler) | ||
logger_mock = mocker.patch("stripe.util.log_warning") | ||
|
||
stripe.proxy = "http://localhost:%s" % self.mock_server_port | ||
stripe.Balance.retrieve() | ||
assert MockServerRequestHandler.num_requests == 1 | ||
|
||
stripe.proxy = "http://bad-url" | ||
logger_mock.assert_not_called() | ||
stripe.Balance.retrieve() | ||
logger_mock.assert_called_with( | ||
"stripe.proxy was updated after sending a request - this is a no-op. To use a different proxy, set stripe.default_http_client to a new client configured with the proxy." | ||
) | ||
assert MockServerRequestHandler.num_requests == 2 | ||
|
||
def test_hits_client_proxy(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we name this one something like |
||
class MockServerRequestHandler(BaseHTTPRequestHandler): | ||
num_requests = 0 | ||
|
||
def do_GET(self): | ||
self.__class__.num_requests += 1 | ||
|
||
self.send_response(200) | ||
self.send_header( | ||
"Content-Type", "application/json; charset=utf-8" | ||
) | ||
self.end_headers() | ||
self.wfile.write(json.dumps({}).encode("utf-8")) | ||
return | ||
|
||
self.setup_mock_server(MockServerRequestHandler) | ||
|
||
stripe.default_http_client = stripe.http_client.new_default_http_client( | ||
proxy="http://localhost:%s" % self.mock_server_port | ||
) | ||
stripe.Balance.retrieve() | ||
assert MockServerRequestHandler.num_requests == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't tell that easily from the changes whether this already happens, but warnings should be emitted by default (if
STRIPE_LOG
isn't set), because otherwise no one will ever see them. Does the code do that right now?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have it respect either
warn
orwarning
? Especially in logging libraries these two are often interchanged that it'd be sort of a nice feature to support both. If we're supporting only one, I'd say that we might want to dowarn
instead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brandur-stripe My code currently requires a log level to be set in order to show warnings. I just did this to follow the existing pattern from
info
anddebug
- I'm fine with changing it.We could simplify the interface and not allow
warn
(orwarning
) at all, and just always print warnings. Is there a case (STRIPE_LOG
/stripe.log
value) where you wouldn't expect warnings to be printed?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be preferable to keep the
STRIPE_LOG
interface unchanged and use thewarnings
module to output warnings, like here. It's more idiomatic and has a better change of being noticed by users.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds a whole lot simpler - I'll do that!