prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Bokeh 3.3.0 uses absolute URLs and breaks proxied dashboards
  I noticed when using `distributed==2023.10.1` with `bokeh==3.2.2` the URLs to the Bokeh JavaScript are relative.
  
  ```html
  <script type="text/javascript" src="static/js/bokeh.min.js?v=3ca6425586de5036dc01992dd69aa61e9196dd02619557cfaeb1b3d8b77adf724be49401b1168483d165494ce57a6daa16e6f6d3660fef117d45028221f86357"></script>
  ```
  
  But when upgrading to `bokeh==3.3.0` they become absolute.
  
  ```html
  <script type="text/javascript" src="/static/js/bokeh.min.js?v=39ef57c3a83533e24f961e5c27f651a61045dbccefac4b5df86a7680b1edaff31886a7c0322250ffb0d758fa14ae156c9b640f60cca99f020096b050a4dbb571"></script>
  ```
  
  This breaks dashboards that are being proxied at some sub-url.
  
  Setting `dask scheduler --dashboard-prefix ""` doesn't fix it.
  
  ### Reproducer
  
  ```
  $ pip install dask distributed bokeh==3.3.0
  $ dask scheduler &
  $ curl localhost:8787/status | grep bokeh.min.js
  ```
  
  </issue>
  <code>
  [start of distributed/dashboard/core.py]
  1 from __future__ import annotations
  2 
  3 import functools
  4 import warnings
  5 
  6 from bokeh.application import Application
  7 from bokeh.application.handlers.function import FunctionHandler
  8 from bokeh.server.server import BokehTornado
  9 from bokeh.server.util import create_hosts_allowlist
  10 
  11 import dask
  12 
  13 from distributed.dashboard.utils import BOKEH_VERSION
  14 from distributed.versions import BOKEH_REQUIREMENT
  15 
  16 # Set `prereleases=True` to allow for use with dev versions of `bokeh`
  17 if not BOKEH_REQUIREMENT.specifier.contains(BOKEH_VERSION, prereleases=True):
  18     warnings.warn(
  19         f"\nDask needs {BOKEH_REQUIREMENT} for the dashboard."
  20         f"\nYou have bokeh={BOKEH_VERSION}."
  21         "\nContinuing without the dashboard."
  22     )
  23     raise ImportError(
  24         f"Dask needs {BOKEH_REQUIREMENT} for the dashboard, not bokeh={BOKEH_VERSION}"
  25     )
  26 
  27 
  28 if BOKEH_VERSION.major < 3:
  29     from bokeh.models import Panel as TabPanel  # noqa: F401
  30 else:
  31     from bokeh.models import TabPanel  # noqa: F401
  32 
  33 
  34 def BokehApplication(applications, server, prefix="/", template_variables=None):
  35     template_variables = template_variables or {}
  36     prefix = "/" + prefix.strip("/") + "/" if prefix else "/"
  37 
  38     extra = {"prefix": prefix, **template_variables}
  39 
  40     funcs = {k: functools.partial(v, server, extra) for k, v in applications.items()}
  41     apps = {k: Application(FunctionHandler(v)) for k, v in funcs.items()}
  42 
  43     kwargs = dask.config.get("distributed.scheduler.dashboard.bokeh-application").copy()
  44     extra_websocket_origins = create_hosts_allowlist(
  45         kwargs.pop("allow_websocket_origin"), server.http_server.port
  46     )
  47 
  48     return BokehTornado(
  49         apps,
  50         prefix=prefix,
  51         use_index=False,
  52         extra_websocket_origins=extra_websocket_origins,
  53         **kwargs,
  54     )
  55
  [end of distributed/dashboard/core.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/distributed/dashboard/core.py b/distributed/dashboard/core.py
  --- a/distributed/dashboard/core.py
  +++ b/distributed/dashboard/core.py
  @@ -5,6 +5,7 @@
   
   from bokeh.application import Application
   from bokeh.application.handlers.function import FunctionHandler
  +from bokeh.resources import Resources
   from bokeh.server.server import BokehTornado
   from bokeh.server.util import create_hosts_allowlist
   
  @@ -31,6 +32,11 @@
       from bokeh.models import TabPanel  # noqa: F401
   
   
  +class DaskBokehTornado(BokehTornado):
  +    def resources(self, absolute_url: str | bool | None = True) -> Resources:
  +        return super().resources(absolute_url)
  +
  +
   def BokehApplication(applications, server, prefix="/", template_variables=None):
       template_variables = template_variables or {}
       prefix = "/" + prefix.strip("/") + "/" if prefix else "/"
  @@ -45,10 +51,11 @@
           kwargs.pop("allow_websocket_origin"), server.http_server.port
       )
   
  -    return BokehTornado(
  +    return DaskBokehTornado(
           apps,
           prefix=prefix,
           use_index=False,
           extra_websocket_origins=extra_websocket_origins,
  +        absolute_url="",
           **kwargs,
       )
  
verification_info: {"golden_diff": "diff --git a/distributed/dashboard/core.py b/distributed/dashboard/core.py\n--- a/distributed/dashboard/core.py\n+++ b/distributed/dashboard/core.py\n@@ -5,6 +5,7 @@\n \n from bokeh.application import Application\n from bokeh.application.handlers.function import FunctionHandler\n+from bokeh.resources import Resources\n from bokeh.server.server import BokehTornado\n from bokeh.server.util import create_hosts_allowlist\n \n@@ -31,6 +32,11 @@\n     from bokeh.models import TabPanel  # noqa: F401\n \n \n+class DaskBokehTornado(BokehTornado):\n+    def resources(self, absolute_url: str | bool | None = True) -> Resources:\n+        return super().resources(absolute_url)\n+\n+\n def BokehApplication(applications, server, prefix=\"/\", template_variables=None):\n     template_variables = template_variables or {}\n     prefix = \"/\" + prefix.strip(\"/\") + \"/\" if prefix else \"/\"\n@@ -45,10 +51,11 @@\n         kwargs.pop(\"allow_websocket_origin\"), server.http_server.port\n     )\n \n-    return BokehTornado(\n+    return DaskBokehTornado(\n         apps,\n         prefix=prefix,\n         use_index=False,\n         extra_websocket_origins=extra_websocket_origins,\n+        absolute_url=\"\",\n         **kwargs,\n     )\n", "issue": "Bokeh 3.3.0 uses absolute URLs and breaks proxied dashboards\nI noticed when using `distributed==2023.10.1` with `bokeh==3.2.2` the URLs to the Bokeh JavaScript are relative.\r\n\r\n```html\r\n<script type=\"text/javascript\" src=\"static/js/bokeh.min.js?v=3ca6425586de5036dc01992dd69aa61e9196dd02619557cfaeb1b3d8b77adf724be49401b1168483d165494ce57a6daa16e6f6d3660fef117d45028221f86357\"></script>\r\n```\r\n\r\nBut when upgrading to `bokeh==3.3.0` they become absolute.\r\n\r\n```html\r\n<script type=\"text/javascript\" src=\"/static/js/bokeh.min.js?v=39ef57c3a83533e24f961e5c27f651a61045dbccefac4b5df86a7680b1edaff31886a7c0322250ffb0d758fa14ae156c9b640f60cca99f020096b050a4dbb571\"></script>\r\n```\r\n\r\nThis breaks dashboards that are being proxied at some sub-url.\r\n\r\nSetting `dask scheduler --dashboard-prefix \"\"` doesn't fix it.\r\n\r\n### Reproducer\r\n\r\n```\r\n$ pip install dask distributed bokeh==3.3.0\r\n$ dask scheduler &\r\n$ curl localhost:8787/status | grep bokeh.min.js\r\n```\n", "before_files": [{"content": "from __future__ import annotations\n\nimport functools\nimport warnings\n\nfrom bokeh.application import Application\nfrom bokeh.application.handlers.function import FunctionHandler\nfrom bokeh.server.server import BokehTornado\nfrom bokeh.server.util import create_hosts_allowlist\n\nimport dask\n\nfrom distributed.dashboard.utils import BOKEH_VERSION\nfrom distributed.versions import BOKEH_REQUIREMENT\n\n# Set `prereleases=True` to allow for use with dev versions of `bokeh`\nif not BOKEH_REQUIREMENT.specifier.contains(BOKEH_VERSION, prereleases=True):\n    warnings.warn(\n        f\"\\nDask needs {BOKEH_REQUIREMENT} for the dashboard.\"\n        f\"\\nYou have bokeh={BOKEH_VERSION}.\"\n        \"\\nContinuing without the dashboard.\"\n    )\n    raise ImportError(\n        f\"Dask needs {BOKEH_REQUIREMENT} for the dashboard, not bokeh={BOKEH_VERSION}\"\n    )\n\n\nif BOKEH_VERSION.major < 3:\n    from bokeh.models import Panel as TabPanel  # noqa: F401\nelse:\n    from bokeh.models import TabPanel  # noqa: F401\n\n\ndef BokehApplication(applications, server, prefix=\"/\", template_variables=None):\n    template_variables = template_variables or {}\n    prefix = \"/\" + prefix.strip(\"/\") + \"/\" if prefix else \"/\"\n\n    extra = {\"prefix\": prefix, **template_variables}\n\n    funcs = {k: functools.partial(v, server, extra) for k, v in applications.items()}\n    apps = {k: Application(FunctionHandler(v)) for k, v in funcs.items()}\n\n    kwargs = dask.config.get(\"distributed.scheduler.dashboard.bokeh-application\").copy()\n    extra_websocket_origins = create_hosts_allowlist(\n        kwargs.pop(\"allow_websocket_origin\"), server.http_server.port\n    )\n\n    return BokehTornado(\n        apps,\n        prefix=prefix,\n        use_index=False,\n        extra_websocket_origins=extra_websocket_origins,\n        **kwargs,\n    )\n", "path": "distributed/dashboard/core.py"}]}