Repository: tornadoweb/tornado

Issue:
httpclient: hide `initialize` method from docs
Hi all! 

I'm currently trying to annotate some code that implements async HTTP servers and clients based on the Tornado framework and I've got these two lines of code:
```
 self._http_client = AsyncHTTPClient(force_instance=True)
 self._http_client.initialize(max_clients=100)
````
that produce this `mypy` error (using tornado `6.1` and mypy `0.950`):
```
error: Unexpected keyword argument "max_clients" for "initialize" of "AsyncHTTPClient"  [call-arg]
```
While trying to fix the error I've first looked into the documentation here:
https://www.tornadoweb.org/en/stable/httpclient.html#tornado.simple_httpclient.SimpleAsyncHTTPClient.initialize

Since the documentation describes a parameter `max_clients`, but `mypy` disagrees, I searched the current code and found that `initialize()` actually looks completely different:
https://github.com/tornadoweb/tornado/blob/2047e7ae3c825bf52dad10cc8402d09e11091bc1/tornado/httpclient.py#L216

Therefore, I have some questions:
1.) is the documentation at https://www.tornadoweb.org outdated? If yes, then why is it still online and linked from the Github project page?
2.) how am I supposed to set `max_clients` in tornado 6.1?


Discussion:
I found the reason for the `mypy` error: the `AsyncHTTPClient` constructor actually returns a `SimpleAsyncHTTPClient` instance, which implements a different `initialize()` function that actually takes a `max_clients` parameter. That level of magic is apparently too high for `mypy` (and not exactly easy to see trough for humans either, imo).

I've fixed it like this:
```
self._http_client: SimpleAsyncHTTPClient = cast(SimpleAsyncHTTPClient, AsyncHTTPClient(force_instance=True))
self._http_client.initialize(max_clients=100)
```
Oh, these docs are bad. You're not supposed to actually call `initialize`; it's supposed to be part of the magic instance-reuse functionality. The arguments to `initialize` are actually the arguments to the constructor. There are two ways to set `max_clients`.

To configure the global singleton:

    AsyncHTTPClient.configure(None, max_clients=100)

To create a new instance:

    client = AsyncHTTPClient(force_instance=True, max_clients=100)

Either way, the type annotations use `**kwargs: Any` so there won't be any compile-time type checks on these parameters. (Yes, there's too much magic here. I wouldn't do things this way again but it's difficult to unwind without breaking backwards compatibility)
Thanks for the clarification :+1: 

Resolving patch:
diff --git a/tornado/simple_httpclient.py b/tornado/simple_httpclient.py
--- a/tornado/simple_httpclient.py
+++ b/tornado/simple_httpclient.py
@@ -84,6 +84,35 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient):
     supported.  In particular, proxies are not supported, connections
     are not reused, and callers cannot select the network interface to be
     used.
+
+    This implementation supports the following arguments, which can be passed
+    to ``configure()`` to control the global singleton, or to the constructor
+    when ``force_instance=True``.
+
+    ``max_clients`` is the number of concurrent requests that can be
+    in progress; when this limit is reached additional requests will be
+    queued. Note that time spent waiting in this queue still counts
+    against the ``request_timeout``.
+
+    ``defaults`` is a dict of parameters that will be used as defaults on all
+    `.HTTPRequest` objects submitted to this client.
+
+    ``hostname_mapping`` is a dictionary mapping hostnames to IP addresses.
+    It can be used to make local DNS changes when modifying system-wide
+    settings like ``/etc/hosts`` is not possible or desirable (e.g. in
+    unittests). ``resolver`` is similar, but using the `.Resolver` interface
+    instead of a simple mapping.
+
+    ``max_buffer_size`` (default 100MB) is the number of bytes
+    that can be read into memory at once. ``max_body_size``
+    (defaults to ``max_buffer_size``) is the largest response body
+    that the client will accept.  Without a
+    ``streaming_callback``, the smaller of these two limits
+    applies; with a ``streaming_callback`` only ``max_body_size``
+    does.
+
+    .. versionchanged:: 4.2
+        Added the ``max_body_size`` argument.
     """
 
     def initialize(  # type: ignore
@@ -96,38 +125,6 @@ def initialize(  # type: ignore
         max_header_size: Optional[int] = None,
         max_body_size: Optional[int] = None,
     ) -> None:
-        """Creates a AsyncHTTPClient.
-
-        Only a single AsyncHTTPClient instance exists per IOLoop
-        in order to provide limitations on the number of pending connections.
-        ``force_instance=True`` may be used to suppress this behavior.
-
-        Note that because of this implicit reuse, unless ``force_instance``
-        is used, only the first call to the constructor actually uses
-        its arguments. It is recommended to use the ``configure`` method
-        instead of the constructor to ensure that arguments take effect.
-
-        ``max_clients`` is the number of concurrent requests that can be
-        in progress; when this limit is reached additional requests will be
-        queued. Note that time spent waiting in this queue still counts
-        against the ``request_timeout``.
-
-        ``hostname_mapping`` is a dictionary mapping hostnames to IP addresses.
-        It can be used to make local DNS changes when modifying system-wide
-        settings like ``/etc/hosts`` is not possible or desirable (e.g. in
-        unittests).
-
-        ``max_buffer_size`` (default 100MB) is the number of bytes
-        that can be read into memory at once. ``max_body_size``
-        (defaults to ``max_buffer_size``) is the largest response body
-        that the client will accept.  Without a
-        ``streaming_callback``, the smaller of these two limits
-        applies; with a ``streaming_callback`` only ``max_body_size``
-        does.
-
-        .. versionchanged:: 4.2
-           Added the ``max_body_size`` argument.
-        """
         super().initialize(defaults=defaults)
         self.max_clients = max_clients
         self.queue = (


Test patch: