diff --git a/requests_mock/mocker.py b/requests_mock/mocker.py index b9e5f5e..095230d 100644 --- a/requests_mock/mocker.py +++ b/requests_mock/mocker.py @@ -10,7 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. +import contextlib import functools +import sys import threading import types @@ -36,6 +38,22 @@ _send_lock = threading.RLock() +@contextlib.contextmanager +def threading_rlock(timeout): + kwargs = {} + if sys.version_info.major >= 3: + # python2 doesn't support the timeout argument + kwargs['timeout'] = timeout + + if not _send_lock.acquire(**kwargs): + raise Exception("Could not acquire threading lock - possible deadlock scenario") + + try: + yield + finally: + _send_lock.release() + + def _is_bound_method(method): """ bound_method 's self is a obj @@ -137,10 +155,7 @@ def _fake_send(session, request, **kwargs): # are multiple threads running - one thread could restore the # original get_adapter() just as a second thread is about to # execute _original_send() below - if not _send_lock.acquire(timeout=10): - raise Exception("Could not acquire threading lock - possible deadlock scenario") - - try: + with threading_rlock(timeout=10): # mock get_adapter # # NOTE(phodge): requests.Session.send() is actually @@ -175,8 +190,6 @@ def _fake_send(session, request, **kwargs): finally: # restore get_adapter _set_method(session, "get_adapter", self._last_get_adapter) - finally: - _send_lock.release() # if we are here it means we must run the real http request # Or, with nested mocks, to the parent mock, that is why we use