prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Stream argument to ProfilerMiddleware is not typed hinted correctly
  The profiler middleware `src/werkzeug/middleware/profiler.py:80` documentation and implementation states that the argument `stream` can be set to `None` to disable output.
   
  However the type hints specifies `stream: t.IO[str] = sys.stdout,` which is inconsistent with the documentation and the implementation.
  
  To replicate the issue run a type hint aware validator against something initiating a `ProfilerMiddleware` with the `stream` argument set to `None`:
  ```
  ProfilerMiddleware(
      app,
      stream=None
  )
  ```
  It will cause an error stating that `None` is not an acceptable value for `stream`.
  
  It's a minor and easy fix that I'm happy to provide a PR for if deemed acceptable with the proposed change:
  ```
  stream: t.Union[t.IO[str], None] = sys.stdout,
  ```
  
  Environment:
  
  - Python version: 3.10
  - Werkzeug version: 2.2.3
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/werkzeug/middleware/profiler.py`
  Content:
  ```
  1 """
  2 Application Profiler
  3 ====================
  4 
  5 This module provides a middleware that profiles each request with the
  6 :mod:`cProfile` module. This can help identify bottlenecks in your code
  7 that may be slowing down your application.
  8 
  9 .. autoclass:: ProfilerMiddleware
  10 
  11 :copyright: 2007 Pallets
  12 :license: BSD-3-Clause
  13 """
  14 import os.path
  15 import sys
  16 import time
  17 import typing as t
  18 from pstats import Stats
  19 
  20 try:
  21     from cProfile import Profile
  22 except ImportError:
  23     from profile import Profile  # type: ignore
  24 
  25 if t.TYPE_CHECKING:
  26     from _typeshed.wsgi import StartResponse
  27     from _typeshed.wsgi import WSGIApplication
  28     from _typeshed.wsgi import WSGIEnvironment
  29 
  30 
  31 class ProfilerMiddleware:
  32     """Wrap a WSGI application and profile the execution of each
  33     request. Responses are buffered so that timings are more exact.
  34 
  35     If ``stream`` is given, :class:`pstats.Stats` are written to it
  36     after each request. If ``profile_dir`` is given, :mod:`cProfile`
  37     data files are saved to that directory, one file per request.
  38 
  39     The filename can be customized by passing ``filename_format``. If
  40     it is a string, it will be formatted using :meth:`str.format` with
  41     the following fields available:
  42 
  43     -   ``{method}`` - The request method; GET, POST, etc.
  44     -   ``{path}`` - The request path or 'root' should one not exist.
  45     -   ``{elapsed}`` - The elapsed time of the request.
  46     -   ``{time}`` - The time of the request.
  47 
  48     If it is a callable, it will be called with the WSGI ``environ``
  49     dict and should return a filename.
  50 
  51     :param app: The WSGI application to wrap.
  52     :param stream: Write stats to this stream. Disable with ``None``.
  53     :param sort_by: A tuple of columns to sort stats by. See
  54         :meth:`pstats.Stats.sort_stats`.
  55     :param restrictions: A tuple of restrictions to filter stats by. See
  56         :meth:`pstats.Stats.print_stats`.
  57     :param profile_dir: Save profile data files to this directory.
  58     :param filename_format: Format string for profile data file names,
  59         or a callable returning a name. See explanation above.
  60 
  61     .. code-block:: python
  62 
  63         from werkzeug.middleware.profiler import ProfilerMiddleware
  64         app = ProfilerMiddleware(app)
  65 
  66     .. versionchanged:: 0.15
  67         Stats are written even if ``profile_dir`` is given, and can be
  68         disable by passing ``stream=None``.
  69 
  70     .. versionadded:: 0.15
  71         Added ``filename_format``.
  72 
  73     .. versionadded:: 0.9
  74         Added ``restrictions`` and ``profile_dir``.
  75     """
  76 
  77     def __init__(
  78         self,
  79         app: "WSGIApplication",
  80         stream: t.IO[str] = sys.stdout,
  81         sort_by: t.Iterable[str] = ("time", "calls"),
  82         restrictions: t.Iterable[t.Union[str, int, float]] = (),
  83         profile_dir: t.Optional[str] = None,
  84         filename_format: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof",
  85     ) -> None:
  86         self._app = app
  87         self._stream = stream
  88         self._sort_by = sort_by
  89         self._restrictions = restrictions
  90         self._profile_dir = profile_dir
  91         self._filename_format = filename_format
  92 
  93     def __call__(
  94         self, environ: "WSGIEnvironment", start_response: "StartResponse"
  95     ) -> t.Iterable[bytes]:
  96         response_body: t.List[bytes] = []
  97 
  98         def catching_start_response(status, headers, exc_info=None):  # type: ignore
  99             start_response(status, headers, exc_info)
  100             return response_body.append
  101 
  102         def runapp() -> None:
  103             app_iter = self._app(
  104                 environ, t.cast("StartResponse", catching_start_response)
  105             )
  106             response_body.extend(app_iter)
  107 
  108             if hasattr(app_iter, "close"):
  109                 app_iter.close()
  110 
  111         profile = Profile()
  112         start = time.time()
  113         profile.runcall(runapp)
  114         body = b"".join(response_body)
  115         elapsed = time.time() - start
  116 
  117         if self._profile_dir is not None:
  118             if callable(self._filename_format):
  119                 filename = self._filename_format(environ)
  120             else:
  121                 filename = self._filename_format.format(
  122                     method=environ["REQUEST_METHOD"],
  123                     path=environ["PATH_INFO"].strip("/").replace("/", ".") or "root",
  124                     elapsed=elapsed * 1000.0,
  125                     time=time.time(),
  126                 )
  127             filename = os.path.join(self._profile_dir, filename)
  128             profile.dump_stats(filename)
  129 
  130         if self._stream is not None:
  131             stats = Stats(profile, stream=self._stream)
  132             stats.sort_stats(*self._sort_by)
  133             print("-" * 80, file=self._stream)
  134             path_info = environ.get("PATH_INFO", "")
  135             print(f"PATH: {path_info!r}", file=self._stream)
  136             stats.print_stats(*self._restrictions)
  137             print(f"{'-' * 80}\n", file=self._stream)
  138 
  139         return [body]
  140
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/werkzeug/middleware/profiler.py b/src/werkzeug/middleware/profiler.py
  --- a/src/werkzeug/middleware/profiler.py
  +++ b/src/werkzeug/middleware/profiler.py
  @@ -77,7 +77,7 @@
       def __init__(
           self,
           app: "WSGIApplication",
  -        stream: t.IO[str] = sys.stdout,
  +        stream: t.Union[t.IO[str], None] = sys.stdout,
           sort_by: t.Iterable[str] = ("time", "calls"),
           restrictions: t.Iterable[t.Union[str, int, float]] = (),
           profile_dir: t.Optional[str] = None,
  
verification_info: {"golden_diff": "diff --git a/src/werkzeug/middleware/profiler.py b/src/werkzeug/middleware/profiler.py\n--- a/src/werkzeug/middleware/profiler.py\n+++ b/src/werkzeug/middleware/profiler.py\n@@ -77,7 +77,7 @@\n     def __init__(\n         self,\n         app: \"WSGIApplication\",\n-        stream: t.IO[str] = sys.stdout,\n+        stream: t.Union[t.IO[str], None] = sys.stdout,\n         sort_by: t.Iterable[str] = (\"time\", \"calls\"),\n         restrictions: t.Iterable[t.Union[str, int, float]] = (),\n         profile_dir: t.Optional[str] = None,\n", "issue": "Stream argument to ProfilerMiddleware is not typed hinted correctly\nThe profiler middleware `src/werkzeug/middleware/profiler.py:80` documentation and implementation states that the argument `stream` can be set to `None` to disable output.\r\n \r\nHowever the type hints specifies `stream: t.IO[str] = sys.stdout,` which is inconsistent with the documentation and the implementation.\r\n\r\nTo replicate the issue run a type hint aware validator against something initiating a `ProfilerMiddleware` with the `stream` argument set to `None`:\r\n```\r\nProfilerMiddleware(\r\n    app,\r\n    stream=None\r\n)\r\n```\r\nIt will cause an error stating that `None` is not an acceptable value for `stream`.\r\n\r\nIt's a minor and easy fix that I'm happy to provide a PR for if deemed acceptable with the proposed change:\r\n```\r\nstream: t.Union[t.IO[str], None] = sys.stdout,\r\n```\r\n\r\nEnvironment:\r\n\r\n- Python version: 3.10\r\n- Werkzeug version: 2.2.3\r\n\n", "before_files": [{"content": "\"\"\"\nApplication Profiler\n====================\n\nThis module provides a middleware that profiles each request with the\n:mod:`cProfile` module. This can help identify bottlenecks in your code\nthat may be slowing down your application.\n\n.. autoclass:: ProfilerMiddleware\n\n:copyright: 2007 Pallets\n:license: BSD-3-Clause\n\"\"\"\nimport os.path\nimport sys\nimport time\nimport typing as t\nfrom pstats import Stats\n\ntry:\n    from cProfile import Profile\nexcept ImportError:\n    from profile import Profile  # type: ignore\n\nif t.TYPE_CHECKING:\n    from _typeshed.wsgi import StartResponse\n    from _typeshed.wsgi import WSGIApplication\n    from _typeshed.wsgi import WSGIEnvironment\n\n\nclass ProfilerMiddleware:\n    \"\"\"Wrap a WSGI application and profile the execution of each\n    request. Responses are buffered so that timings are more exact.\n\n    If ``stream`` is given, :class:`pstats.Stats` are written to it\n    after each request. If ``profile_dir`` is given, :mod:`cProfile`\n    data files are saved to that directory, one file per request.\n\n    The filename can be customized by passing ``filename_format``. If\n    it is a string, it will be formatted using :meth:`str.format` with\n    the following fields available:\n\n    -   ``{method}`` - The request method; GET, POST, etc.\n    -   ``{path}`` - The request path or 'root' should one not exist.\n    -   ``{elapsed}`` - The elapsed time of the request.\n    -   ``{time}`` - The time of the request.\n\n    If it is a callable, it will be called with the WSGI ``environ``\n    dict and should return a filename.\n\n    :param app: The WSGI application to wrap.\n    :param stream: Write stats to this stream. Disable with ``None``.\n    :param sort_by: A tuple of columns to sort stats by. See\n        :meth:`pstats.Stats.sort_stats`.\n    :param restrictions: A tuple of restrictions to filter stats by. See\n        :meth:`pstats.Stats.print_stats`.\n    :param profile_dir: Save profile data files to this directory.\n    :param filename_format: Format string for profile data file names,\n        or a callable returning a name. See explanation above.\n\n    .. code-block:: python\n\n        from werkzeug.middleware.profiler import ProfilerMiddleware\n        app = ProfilerMiddleware(app)\n\n    .. versionchanged:: 0.15\n        Stats are written even if ``profile_dir`` is given, and can be\n        disable by passing ``stream=None``.\n\n    .. versionadded:: 0.15\n        Added ``filename_format``.\n\n    .. versionadded:: 0.9\n        Added ``restrictions`` and ``profile_dir``.\n    \"\"\"\n\n    def __init__(\n        self,\n        app: \"WSGIApplication\",\n        stream: t.IO[str] = sys.stdout,\n        sort_by: t.Iterable[str] = (\"time\", \"calls\"),\n        restrictions: t.Iterable[t.Union[str, int, float]] = (),\n        profile_dir: t.Optional[str] = None,\n        filename_format: str = \"{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof\",\n    ) -> None:\n        self._app = app\n        self._stream = stream\n        self._sort_by = sort_by\n        self._restrictions = restrictions\n        self._profile_dir = profile_dir\n        self._filename_format = filename_format\n\n    def __call__(\n        self, environ: \"WSGIEnvironment\", start_response: \"StartResponse\"\n    ) -> t.Iterable[bytes]:\n        response_body: t.List[bytes] = []\n\n        def catching_start_response(status, headers, exc_info=None):  # type: ignore\n            start_response(status, headers, exc_info)\n            return response_body.append\n\n        def runapp() -> None:\n            app_iter = self._app(\n                environ, t.cast(\"StartResponse\", catching_start_response)\n            )\n            response_body.extend(app_iter)\n\n            if hasattr(app_iter, \"close\"):\n                app_iter.close()\n\n        profile = Profile()\n        start = time.time()\n        profile.runcall(runapp)\n        body = b\"\".join(response_body)\n        elapsed = time.time() - start\n\n        if self._profile_dir is not None:\n            if callable(self._filename_format):\n                filename = self._filename_format(environ)\n            else:\n                filename = self._filename_format.format(\n                    method=environ[\"REQUEST_METHOD\"],\n                    path=environ[\"PATH_INFO\"].strip(\"/\").replace(\"/\", \".\") or \"root\",\n                    elapsed=elapsed * 1000.0,\n                    time=time.time(),\n                )\n            filename = os.path.join(self._profile_dir, filename)\n            profile.dump_stats(filename)\n\n        if self._stream is not None:\n            stats = Stats(profile, stream=self._stream)\n            stats.sort_stats(*self._sort_by)\n            print(\"-\" * 80, file=self._stream)\n            path_info = environ.get(\"PATH_INFO\", \"\")\n            print(f\"PATH: {path_info!r}\", file=self._stream)\n            stats.print_stats(*self._restrictions)\n            print(f\"{'-' * 80}\\n\", file=self._stream)\n\n        return [body]\n", "path": "src/werkzeug/middleware/profiler.py"}], "after_files": [{"content": "\"\"\"\nApplication Profiler\n====================\n\nThis module provides a middleware that profiles each request with the\n:mod:`cProfile` module. This can help identify bottlenecks in your code\nthat may be slowing down your application.\n\n.. autoclass:: ProfilerMiddleware\n\n:copyright: 2007 Pallets\n:license: BSD-3-Clause\n\"\"\"\nimport os.path\nimport sys\nimport time\nimport typing as t\nfrom pstats import Stats\n\ntry:\n    from cProfile import Profile\nexcept ImportError:\n    from profile import Profile  # type: ignore\n\nif t.TYPE_CHECKING:\n    from _typeshed.wsgi import StartResponse\n    from _typeshed.wsgi import WSGIApplication\n    from _typeshed.wsgi import WSGIEnvironment\n\n\nclass ProfilerMiddleware:\n    \"\"\"Wrap a WSGI application and profile the execution of each\n    request. Responses are buffered so that timings are more exact.\n\n    If ``stream`` is given, :class:`pstats.Stats` are written to it\n    after each request. If ``profile_dir`` is given, :mod:`cProfile`\n    data files are saved to that directory, one file per request.\n\n    The filename can be customized by passing ``filename_format``. If\n    it is a string, it will be formatted using :meth:`str.format` with\n    the following fields available:\n\n    -   ``{method}`` - The request method; GET, POST, etc.\n    -   ``{path}`` - The request path or 'root' should one not exist.\n    -   ``{elapsed}`` - The elapsed time of the request.\n    -   ``{time}`` - The time of the request.\n\n    If it is a callable, it will be called with the WSGI ``environ``\n    dict and should return a filename.\n\n    :param app: The WSGI application to wrap.\n    :param stream: Write stats to this stream. Disable with ``None``.\n    :param sort_by: A tuple of columns to sort stats by. See\n        :meth:`pstats.Stats.sort_stats`.\n    :param restrictions: A tuple of restrictions to filter stats by. See\n        :meth:`pstats.Stats.print_stats`.\n    :param profile_dir: Save profile data files to this directory.\n    :param filename_format: Format string for profile data file names,\n        or a callable returning a name. See explanation above.\n\n    .. code-block:: python\n\n        from werkzeug.middleware.profiler import ProfilerMiddleware\n        app = ProfilerMiddleware(app)\n\n    .. versionchanged:: 0.15\n        Stats are written even if ``profile_dir`` is given, and can be\n        disable by passing ``stream=None``.\n\n    .. versionadded:: 0.15\n        Added ``filename_format``.\n\n    .. versionadded:: 0.9\n        Added ``restrictions`` and ``profile_dir``.\n    \"\"\"\n\n    def __init__(\n        self,\n        app: \"WSGIApplication\",\n        stream: t.Union[t.IO[str], None] = sys.stdout,\n        sort_by: t.Iterable[str] = (\"time\", \"calls\"),\n        restrictions: t.Iterable[t.Union[str, int, float]] = (),\n        profile_dir: t.Optional[str] = None,\n        filename_format: str = \"{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof\",\n    ) -> None:\n        self._app = app\n        self._stream = stream\n        self._sort_by = sort_by\n        self._restrictions = restrictions\n        self._profile_dir = profile_dir\n        self._filename_format = filename_format\n\n    def __call__(\n        self, environ: \"WSGIEnvironment\", start_response: \"StartResponse\"\n    ) -> t.Iterable[bytes]:\n        response_body: t.List[bytes] = []\n\n        def catching_start_response(status, headers, exc_info=None):  # type: ignore\n            start_response(status, headers, exc_info)\n            return response_body.append\n\n        def runapp() -> None:\n            app_iter = self._app(\n                environ, t.cast(\"StartResponse\", catching_start_response)\n            )\n            response_body.extend(app_iter)\n\n            if hasattr(app_iter, \"close\"):\n                app_iter.close()\n\n        profile = Profile()\n        start = time.time()\n        profile.runcall(runapp)\n        body = b\"\".join(response_body)\n        elapsed = time.time() - start\n\n        if self._profile_dir is not None:\n            if callable(self._filename_format):\n                filename = self._filename_format(environ)\n            else:\n                filename = self._filename_format.format(\n                    method=environ[\"REQUEST_METHOD\"],\n                    path=environ[\"PATH_INFO\"].strip(\"/\").replace(\"/\", \".\") or \"root\",\n                    elapsed=elapsed * 1000.0,\n                    time=time.time(),\n                )\n            filename = os.path.join(self._profile_dir, filename)\n            profile.dump_stats(filename)\n\n        if self._stream is not None:\n            stats = Stats(profile, stream=self._stream)\n            stats.sort_stats(*self._sort_by)\n            print(\"-\" * 80, file=self._stream)\n            path_info = environ.get(\"PATH_INFO\", \"\")\n            print(f\"PATH: {path_info!r}\", file=self._stream)\n            stats.print_stats(*self._restrictions)\n            print(f\"{'-' * 80}\\n\", file=self._stream)\n\n        return [body]\n", "path": "src/werkzeug/middleware/profiler.py"}]}