From 3c48b3f55470e006f82bdcde3d51037c31a240de Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 12:05:33 -0400 Subject: [PATCH 1/6] Avoid loading twisted at module import time --- raven/transport/twisted.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/raven/transport/twisted.py b/raven/transport/twisted.py index e3f19c539..74ab7e60c 100644 --- a/raven/transport/twisted.py +++ b/raven/transport/twisted.py @@ -7,26 +7,22 @@ """ from __future__ import absolute_import - from raven.utils.compat import BytesIO from raven.transport.base import AsyncTransport from raven.transport.http import HTTPTransport -try: - from twisted.web.client import ( - Agent, FileBodyProducer, HTTPConnectionPool, ResponseNeverReceived, - readBody, - ) - from twisted.web.http_headers import Headers - has_twisted = True -except ImportError: - has_twisted = False - class TwistedHTTPTransport(AsyncTransport, HTTPTransport): scheme = ['twisted+http', 'twisted+https'] def __init__(self, *args, **kwargs): + try: + from twisted.web.client import Agent, HTTPConnectionPool + import twisted.web.http_headers # NOQA + has_twisted = True + except ImportError: + has_twisted = False + if not has_twisted: raise ImportError('TwistedHTTPTransport requires twisted.web.') @@ -39,6 +35,9 @@ def __init__(self, *args, **kwargs): self._agent = Agent(reactor, pool=HTTPConnectionPool(reactor)) def async_send(self, url, data, headers, success_cb, failure_cb): + from twisted.web.client import FileBodyProducer, ResponseNeverReceived + from twisted.web.http_headers import Headers + d = self._agent.request( b"POST", url, bodyProducer=FileBodyProducer(BytesIO(data)), @@ -58,6 +57,8 @@ def on_success(response): Success only means that the request succeeded, *not* that the actual submission was successful. """ + from twisted.web.client import readBody + if response.code == 200: success_cb() else: From 44e27d300d9226512f8a411071eb26bc5a41678a Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 12:05:49 -0400 Subject: [PATCH 2/6] Avoid loading requests at module import time --- raven/transport/requests.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/raven/transport/requests.py b/raven/transport/requests.py index 8310ef380..14ee3415b 100644 --- a/raven/transport/requests.py +++ b/raven/transport/requests.py @@ -9,24 +9,25 @@ from raven.transport.http import HTTPTransport -try: - import requests - has_requests = True -except ImportError: - has_requests = False - class RequestsHTTPTransport(HTTPTransport): scheme = ['requests+http', 'requests+https'] def __init__(self, *args, **kwargs): + try: + import requests # NOQA + has_requests = True + except ImportError: + has_requests = False + if not has_requests: raise ImportError('RequestsHTTPTransport requires requests.') super(RequestsHTTPTransport, self).__init__(*args, **kwargs) def send(self, url, data, headers): + import requests if self.verify_ssl: # If SSL verification is enabled use the provided CA bundle to # perform the verification. From eeb6a46112353973e72d2c1548a48a74f0d1a138 Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 12:14:01 -0400 Subject: [PATCH 3/6] Delay transport discovery until after module load --- raven/conf/remote.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/raven/conf/remote.py b/raven/conf/remote.py index af4cb4b35..99471ddc0 100644 --- a/raven/conf/remote.py +++ b/raven/conf/remote.py @@ -33,9 +33,6 @@ def discover_default_transport(): return ThreadedHTTPTransport -DEFAULT_TRANSPORT = discover_default_transport() - - class RemoteConfig(object): def __init__(self, base_url=None, project=None, public_key=None, secret_key=None, transport=None, options=None): @@ -52,7 +49,7 @@ def __init__(self, base_url=None, project=None, public_key=None, self.options = options or {} self.store_endpoint = store_endpoint - self._transport_cls = transport or DEFAULT_TRANSPORT + self._transport_cls = transport or discover_default_transport() def __unicode__(self): return text_type(self.base_url) From 73c1cf05da116b27058589e3d685d463169487d9 Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 12:15:25 -0400 Subject: [PATCH 4/6] Delay hooking libraries until after they're imported --- raven/breadcrumbs.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/raven/breadcrumbs.py b/raven/breadcrumbs.py index 13987f133..10ea87958 100644 --- a/raven/breadcrumbs.py +++ b/raven/breadcrumbs.py @@ -1,7 +1,13 @@ from __future__ import absolute_import +try: + import __builtin__ as builtins +except ImportError: + import builtins + import os import logging +import sys from time import time from types import FunctionType @@ -295,6 +301,16 @@ def register_logging_handler(callback): hooked_libraries = {} +delayed_hooks = {} +_orig_import = __import__ + + +def delayed_hook_libraries(name, globals={}, locals={}, fromlist=[], level=-1): + result = _orig_import(name, globals, locals, fromlist, level) + if name in delayed_hooks: + func = delayed_hooks[name] + func() + return result def libraryhook(name): @@ -390,7 +406,12 @@ def hook_libraries(libraries): func = hooked_libraries.get(lib) if func is None: raise RuntimeError('Unknown library %r for hooking' % lib) - func() + if lib in sys.modules: + func() # lib is already loaded + else: + delayed_hooks[lib] = func + if delayed_hooks: + builtins.__import__ = delayed_hook_libraries import raven.context From c1289b42f6ffb17ac3c373ed1923c929ef5d6c80 Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 12:53:01 -0400 Subject: [PATCH 5/6] Allow tests to pass --- raven/transport/requests.py | 2 +- tests/breadcrumbs/tests.py | 2 ++ tests/transport/requests/test_threaded_requests.py | 5 ++++- tests/transport/requests/tests.py | 3 ++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/raven/transport/requests.py b/raven/transport/requests.py index 14ee3415b..6beb96a0a 100644 --- a/raven/transport/requests.py +++ b/raven/transport/requests.py @@ -33,4 +33,4 @@ def send(self, url, data, headers): # perform the verification. self.verify_ssl = self.ca_certs requests.post(url, data=data, headers=headers, - verify=self.verify_ssl, timeout=self.timeout) + verify=self.verify_ssl, timeout=self.timeout) diff --git a/tests/breadcrumbs/tests.py b/tests/breadcrumbs/tests.py index da291e5bd..265ebb9eb 100644 --- a/tests/breadcrumbs/tests.py +++ b/tests/breadcrumbs/tests.py @@ -187,6 +187,8 @@ def new_func(self): crumbs = client.context.breadcrumbs.get_buffer() assert 'dummy' not in set([i['type'] for i in crumbs]) + # pretend 'dummy' is a module, and is already loaded + sys.modules['dummy'] = None client = Client('http://foo:bar@example.com/0', hook_libraries=['requests', 'dummy']) with client.context: DummyClass().dummy_method() diff --git a/tests/transport/requests/test_threaded_requests.py b/tests/transport/requests/test_threaded_requests.py index 2147962ba..9e817d91a 100644 --- a/tests/transport/requests/test_threaded_requests.py +++ b/tests/transport/requests/test_threaded_requests.py @@ -1,5 +1,8 @@ import mock import time + +import requests + from raven.utils.testutils import TestCase from raven.base import Client @@ -26,7 +29,7 @@ def setUp(self): self.url = "threaded+requests+http://some_username:some_password@localhost:8143/1" self.client = Client(dsn=self.url) - @mock.patch('raven.transport.requests.post') + @mock.patch('requests.post') def test_does_send(self, send): self.client.captureMessage(message='foo') diff --git a/tests/transport/requests/tests.py b/tests/transport/requests/tests.py index 97bc48314..1e58c6fba 100644 --- a/tests/transport/requests/tests.py +++ b/tests/transport/requests/tests.py @@ -4,6 +4,7 @@ from raven.utils.testutils import TestCase from raven.base import Client +import requests class RequestsTransportTest(TestCase): @@ -12,7 +13,7 @@ def setUp(self): dsn="requests+http://some_username:some_password@localhost:8143/1", ) - @mock.patch('raven.transport.requests.post') + @mock.patch('requests.post') def test_does_send(self, post): self.client.captureMessage(message='foo') self.assertEqual(post.call_count, 1) From d0ba90ee01191bf06f00b56fbb45796e86cf79fd Mon Sep 17 00:00:00 2001 From: Jonah Petri Date: Mon, 11 Jun 2018 15:30:56 -0400 Subject: [PATCH 6/6] Add workaround for python 3.x --- raven/breadcrumbs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/raven/breadcrumbs.py b/raven/breadcrumbs.py index 10ea87958..8d5131916 100644 --- a/raven/breadcrumbs.py +++ b/raven/breadcrumbs.py @@ -306,7 +306,11 @@ def register_logging_handler(callback): def delayed_hook_libraries(name, globals={}, locals={}, fromlist=[], level=-1): - result = _orig_import(name, globals, locals, fromlist, level) + if level == -1: + result = _orig_import(name, globals, locals, fromlist) # python 3.x workaround + else: + result = _orig_import(name, globals, locals, fromlist, level) + if name in delayed_hooks: func = delayed_hooks[name] func()