]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add a new method to configure AsyncHTTPClient.
authorBen Darnell <ben@bendarnell.com>
Wed, 23 Feb 2011 22:50:43 +0000 (14:50 -0800)
committerBen Darnell <ben@bendarnell.com>
Wed, 23 Feb 2011 22:50:43 +0000 (14:50 -0800)
Deprecate most keyword arguments to AsyncHTTPClient in favor of the new
configure method.

tornado/httpclient.py
tornado/testing.py

index 48faff2c5bc91a37dada91168c8393d5515a3de3..0c6515a081f1424bb662609ce2bcded4c076855d 100644 (file)
@@ -6,6 +6,7 @@ import weakref
 from tornado.escape import utf8
 from tornado import httputil
 from tornado.ioloop import IOLoop
+from tornado.util import import_object
 
 class HTTPClient(object):
     """A blocking HTTP client.
@@ -66,24 +67,51 @@ class AsyncHTTPClient(object):
     fetch() can take a string URL or an HTTPRequest instance, which offers
     more options, like executing POST/PUT/DELETE requests.
 
-    The keyword argument max_clients to the AsyncHTTPClient constructor
-    determines the maximum number of simultaneous fetch() operations that
-    can execute in parallel on each IOLoop.
+    The constructor for this class is magic in several respects:  It actually
+    creates an instance of an implementation-specific subclass, and instances
+    are reused as a kind of pseudo-singleton (one per IOLoop).  The keyword
+    argument force_instance=True can be used to suppress this singleton
+    behavior.  Constructor arguments other than io_loop and force_instance
+    are deprecated.  The implementation subclass as well as arguments to
+    its constructor can be set with the static method configure()
     """
-    _ASYNC_CLIENTS = weakref.WeakKeyDictionary()
+    _async_clients = weakref.WeakKeyDictionary()
+    _impl_class = None
+    _impl_kwargs = None
 
     def __new__(cls, io_loop=None, max_clients=10, force_instance=False, 
                 **kwargs):
         io_loop = io_loop or IOLoop.instance()
-        if io_loop in cls._ASYNC_CLIENTS and not force_instance:
-            return cls._ASYNC_CLIENTS[io_loop]
+        if io_loop in cls._async_clients and not force_instance:
+            return cls._async_clients[io_loop]
         else:
             if cls is AsyncHTTPClient:
-                cls = AsyncImpl
-            instance = super(AsyncHTTPClient, cls).__new__(cls)
-            instance.initialize(io_loop, max_clients, **kwargs)
+                if cls._impl_class is None:
+                    # If the environment variable
+                    # USE_SIMPLE_HTTPCLIENT is set to a non-empty
+                    # string, use simple_httpclient instead of
+                    # curl_httpclient.  This is provided as a
+                    # convenience for testing simple_httpclient, and
+                    # may be removed or replaced with a better way of
+                    # specifying the preferred HTTPClient
+                    # implementation before the next release.
+                    if os.environ.get("USE_SIMPLE_HTTPCLIENT"):
+                        from tornado.simple_httpclient import SimpleAsyncHTTPClient
+                        AsyncHTTPClient._impl_class = SimpleAsyncHTTPClient
+                    else:
+                        from tornado.curl_httpclient import CurlAsyncHTTPClient
+                        AsyncHTTPClient._impl_class = CurlAsyncHTTPClient
+                impl = cls._impl_class
+            else:
+                impl = cls
+            instance = super(AsyncHTTPClient, cls).__new__(impl)
+            args = {}
+            if cls._impl_kwargs:
+                args.update(cls._impl_kwargs)
+            args.update(kwargs)
+            instance.initialize(io_loop, max_clients, **args)
             if not force_instance:
-                cls._ASYNC_CLIENTS[io_loop] = instance
+                cls._async_clients[io_loop] = instance
             return instance
 
     def close(self):
@@ -92,8 +120,8 @@ class AsyncHTTPClient(object):
         create and destroy http clients.  No other methods may be called
         on the AsyncHTTPClient after close().
         """
-        if self._ASYNC_CLIENTS[self.io_loop] is self:
-            del self._ASYNC_CLIENTS[self.io_loop]
+        if self._async_clients[self.io_loop] is self:
+            del self._async_clients[self.io_loop]
 
     def fetch(self, request, callback, **kwargs):
         """Executes an HTTPRequest, calling callback with an HTTPResponse.
@@ -105,6 +133,29 @@ class AsyncHTTPClient(object):
         """
         raise NotImplementedError()
 
+    @staticmethod
+    def configure(impl, **kwargs):
+        """Configures the AsyncHTTPClient subclass to use.
+
+        AsyncHTTPClient() actually creates an instance of a subclass.
+        This method may be called with either a class object or the
+        fully-qualified name of such a class (or None to use the default,
+        SimpleAsyncHTTPClient)
+
+        If additional keyword arguments are given, they will be passed
+        to the constructor of each subclass instance created.  The
+        keyword argument max_clients determines the maximum number of
+        simultaneous fetch() operations that can execute in parallel
+        on each IOLoop.  Additional arguments may be supported depending
+        on the implementation class in use.
+        """
+        if isinstance(impl, basestring):
+            impl = import_object(impl)
+        if impl is not None and not issubclass(impl, AsyncHTTPClient):
+            raise ValueError("Invalid AsyncHTTPClient implementation")
+        AsyncHTTPClient._impl_class = impl
+        AsyncHTTPClient._impl_kwargs = kwargs
+
 class HTTPRequest(object):
     def __init__(self, url, method="GET", headers=None, body=None,
                  auth_username=None, auth_password=None,
@@ -269,15 +320,5 @@ def main():
         if options.print_body:
             print response.body
 
-# If the environment variable USE_SIMPLE_HTTPCLIENT is set to a non-empty
-# string, use simple_httpclient instead of curl_httpclient.
-# This is provided as a convenience for testing simple_httpclient,
-# and may be removed or replaced with a better way of specifying the preferred
-# HTTPClient implementation before the next release.
-if os.environ.get("USE_SIMPLE_HTTPCLIENT"):
-    from tornado.simple_httpclient import SimpleAsyncHTTPClient as AsyncImpl
-else:
-    from tornado.curl_httpclient import CurlAsyncHTTPClient as AsyncImpl
-
 if __name__ == "__main__":
     main()
index 485f840c724d1e7b52454034a885a68e559f8324..507a378e05341a0a37add2bc789b7be2b0028565 100644 (file)
@@ -323,7 +323,13 @@ def main():
     from tornado.options import define, options, parse_command_line
 
     define('autoreload', type=bool, default=False)
+    define('httpclient', type=str, default=None)
     argv = [sys.argv[0]] + parse_command_line(sys.argv)
+
+    if options.httpclient:
+        from tornado.httpclient import AsyncHTTPClient
+        AsyncHTTPClient.configure(options.httpclient)
+
     if __name__ == '__main__' and len(argv) == 1:
         print >> sys.stderr, "No tests specified"
         sys.exit(1)