Skip to content
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

Avoid force_reset to make patching thread-safe #213

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def run_tests(self):
sys.exit(errno)


install_requires = ['PyYAML', 'wrapt', 'six>=1.5']
install_requires = ['PyYAML', 'wrapt', 'six>=1.5', 'byteplay']


extras_require = {
Expand Down
32 changes: 32 additions & 0 deletions vcr/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,35 @@
from .compat import contextlib, mock
from .stubs import VCRHTTPConnection, VCRHTTPSConnection
from six.moves import http_client as httplib
from byteplay import LOAD_GLOBAL, LOAD_ATTR, Code


def _patch_name(func, old, new):
"""Replace references to old with new in func bytecode

This effectively renames variables and attributes in the code for
the function passed.
"""
bytecode = Code.from_code(func.func_code)
for i, (opcode, arg) in enumerate(bytecode.code):
if opcode in [LOAD_GLOBAL, LOAD_ATTR] and arg == old:
bytecode.code[i] = (opcode, new)
func.func_code = bytecode.to_code()


# Save some of the original types for the purposes of unpatching
_HTTPConnection = httplib.HTTPConnection
_HTTPSConnection = httplib.HTTPSConnection

httplib._HTTPConnection = _HTTPConnection
httplib._HTTPSConnection = _HTTPSConnection
# Make the original HTTPSConnection class refer to the original
# HTTPConnection class, always. This means that we don't need to
# unpatch things when creating HTTPSConnection instances.
_patch_name(_HTTPSConnection.__init__.im_func,
'HTTPConnection', '_HTTPConnection')
_patch_name(_HTTPSConnection.connect.im_func,
'HTTPConnection', '_HTTPConnection')

# Try to save the original types for requests
try:
Expand Down Expand Up @@ -42,6 +65,12 @@
_HTTPSConnectionWithTimeout = httplib2.HTTPSConnectionWithTimeout
_SCHEME_TO_CONNECTION = httplib2.SCHEME_TO_CONNECTION

# Make the original classes refer to the original httplib classes.
_patch_name(_HTTPConnectionWithTimeout.__init__.im_func,
'HTTPConnection', '_HTTPConnection')
_patch_name(_HTTPSConnectionWithTimeout.__init__.im_func,
'HTTPSConnection', '_HTTPSConnection')


# Try to save the original types for boto
try:
Expand All @@ -50,6 +79,9 @@
pass
else:
_CertValidatingHTTPSConnection = boto.https_connection.CertValidatingHTTPSConnection
# Make the original class refer to the original httplib class.
_patch_name(_CertValidatingHTTPSConnection.__init__.im_func,
'HTTPConnection', '_HTTPConnection')


# Try to save the original types for Tornado
Expand Down
23 changes: 7 additions & 16 deletions vcr/stubs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,16 +244,12 @@ def getresponse(self, _=False, **kwargs):
self._vcr_request
)
)
# This is imported here to avoid circular import.
# TODO(@IvanMalison): Refactor to allow normal import.
from vcr.patch import force_reset
with force_reset():
self.real_connection.request(
method=self._vcr_request.method,
url=self._url(self._vcr_request.uri),
body=self._vcr_request.body,
headers=self._vcr_request.headers,
)
self.real_connection.request(
method=self._vcr_request.method,
url=self._url(self._vcr_request.uri),
body=self._vcr_request.body,
headers=self._vcr_request.headers,
)

# get the response
response = self.real_connection.getresponse()
Expand Down Expand Up @@ -308,12 +304,7 @@ def __init__(self, *args, **kwargs):
if six.PY3:
kwargs.pop('strict', None) # apparently this is gone in py3

# need to temporarily reset here because the real connection
# inherits from the thing that we are mocking out. Take out
# the reset if you want to see what I mean :)
from vcr.patch import force_reset
with force_reset():
self.real_connection = self._baseclass(*args, **kwargs)
self.real_connection = self._baseclass(*args, **kwargs)

def __setattr__(self, name, value):
"""
Expand Down