diff --git a/raven/breadcrumbs.py b/raven/breadcrumbs.py index 13987f133..8d5131916 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,20 @@ def register_logging_handler(callback): hooked_libraries = {} +delayed_hooks = {} +_orig_import = __import__ + + +def delayed_hook_libraries(name, globals={}, locals={}, fromlist=[], level=-1): + 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() + return result def libraryhook(name): @@ -390,7 +410,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 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) diff --git a/raven/transport/requests.py b/raven/transport/requests.py index 8310ef380..6beb96a0a 100644 --- a/raven/transport/requests.py +++ b/raven/transport/requests.py @@ -9,27 +9,28 @@ 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. 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/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: 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)