prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  webdriver failing to find installed firefox/geckodriver
  Can't get export examples running on binder even though everything is installed and on the path:
  
  <img width="1130" alt="Screen Shot 2020-06-02 at 8 23 16 PM" src="https://user-images.githubusercontent.com/1078448/83592297-e9459c80-a50e-11ea-90d1-7189fcc93af0.png">
  
  
  
  </issue>
  <code>
  [start of bokeh/io/webdriver.py]
  1 #-----------------------------------------------------------------------------
  2 # Copyright (c) 2012 - 2020, Anaconda, Inc., and Bokeh Contributors.
  3 # All rights reserved.
  4 #
  5 # The full license is in the file LICENSE.txt, distributed with this software.
  6 #-----------------------------------------------------------------------------
  7 '''
  8 
  9 '''
  10 
  11 #-----------------------------------------------------------------------------
  12 # Boilerplate
  13 #-----------------------------------------------------------------------------
  14 import logging # isort:skip
  15 log = logging.getLogger(__name__)
  16 
  17 #-----------------------------------------------------------------------------
  18 # Imports
  19 #-----------------------------------------------------------------------------
  20 
  21 from ..util.dependencies import import_required # isort:skip
  22 import_required("selenium.webdriver",
  23                 "To use bokeh.io image export functions you need selenium "
  24                 "('conda install selenium' or 'pip install selenium')")
  25 
  26 # Standard library imports
  27 import atexit
  28 import shutil
  29 from os.path import devnull
  30 from typing import List, Optional
  31 
  32 # External imports
  33 from selenium import webdriver
  34 from selenium.webdriver.remote.webdriver import WebDriver
  35 from typing_extensions import Literal
  36 
  37 #-----------------------------------------------------------------------------
  38 # Globals and constants
  39 #-----------------------------------------------------------------------------
  40 
  41 DriverKind = Literal["firefox", "chromium"]
  42 
  43 __all__ = (
  44     'webdriver_control',
  45 )
  46 
  47 #-----------------------------------------------------------------------------
  48 # General API
  49 #-----------------------------------------------------------------------------
  50 
  51 #-----------------------------------------------------------------------------
  52 # Dev API
  53 #-----------------------------------------------------------------------------
  54 
  55 def create_firefox_webdriver() -> WebDriver:
  56     from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
  57     binary = FirefoxBinary(_detect("firefox"))
  58     options = webdriver.firefox.options.Options()
  59     options.add_argument("--headless")
  60     return webdriver.Firefox(firefox_binary=binary, options=options, service_log_path=devnull)
  61 
  62 def create_chromium_webdriver() -> WebDriver:
  63     options = webdriver.chrome.options.Options()
  64     options.add_argument("--headless")
  65     options.add_argument("--hide-scrollbars")
  66     options.add_argument("--force-device-scale-factor=1")
  67     options.add_argument("--force-color-profile=srgb")
  68     return webdriver.Chrome(options=options)
  69 
  70 #-----------------------------------------------------------------------------
  71 # Private API
  72 #-----------------------------------------------------------------------------
  73 
  74 def _detect(executable: str) -> Optional[str]:
  75     return shutil.which(executable)
  76 
  77 def _try_create_firefox_webdriver() -> Optional[WebDriver]:
  78     try:
  79         return create_firefox_webdriver()
  80     except Exception:
  81         return None
  82 
  83 def _try_create_chromium_webdriver() -> Optional[WebDriver]:
  84     try:
  85         return create_chromium_webdriver()
  86     except Exception:
  87         return None
  88 
  89 class _WebdriverState:
  90     '''
  91 
  92     '''
  93 
  94     reuse: bool
  95     kind: Optional[DriverKind]
  96 
  97     current: Optional[WebDriver]
  98     _drivers: List[WebDriver]
  99 
  100     def __init__(self, *, kind: Optional[DriverKind] = None, reuse: bool = True):
  101         self.kind = kind
  102         self.reuse = reuse
  103         self.current = None
  104         self._drivers = set()
  105 
  106     def terminate(self, driver: WebDriver) -> None:
  107         self._drivers.remove(driver)
  108         driver.quit()
  109 
  110     def reset(self) -> None:
  111         if self.current is not None:
  112             self.terminate(self.current)
  113             self.current = None
  114 
  115     def get(self) -> WebDriver:
  116         if not self.reuse or self.current is None:
  117             self.reset()
  118             self.current = self.create()
  119         return self.current
  120 
  121     def create(self, kind: Optional[DriverKind] = None) -> WebDriver:
  122         driver = self._create(kind)
  123         self._drivers.add(driver)
  124         return driver
  125 
  126     def _create(self, kind: Optional[DriverKind]) -> WebDriver:
  127         driver_kind = kind or self.kind
  128 
  129         if driver_kind is None:
  130             driver = _try_create_chromium_webdriver()
  131             if driver is not None:
  132                 self.kind = "chromium"
  133                 return driver
  134 
  135             driver = _try_create_firefox_webdriver()
  136             if driver is not None:
  137                 self.kind = "firefox"
  138                 return driver
  139 
  140             raise RuntimeError("Neither firefox and geckodriver nor a variant of chromium browser and " \
  141                                "chromedriver are available on system PATH. You can install the former " \
  142                                "with 'conda install -c conda-forge firefox geckodriver'.")
  143         elif driver_kind == "chromium":
  144             return create_chromium_webdriver()
  145         elif driver_kind == "firefox":
  146             return create_firefox_webdriver()
  147         else:
  148             raise ValueError(f"'{driver_kind}' is not a recognized webdriver kind")
  149 
  150     def cleanup(self) -> None:
  151         self.reset()
  152         for driver in list(self._drivers):
  153             self.terminate(driver)
  154 
  155 #-----------------------------------------------------------------------------
  156 # Code
  157 #-----------------------------------------------------------------------------
  158 
  159 webdriver_control = _WebdriverState()
  160 
  161 atexit.register(lambda: webdriver_control.cleanup())
  162
  [end of bokeh/io/webdriver.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/bokeh/io/webdriver.py b/bokeh/io/webdriver.py
  --- a/bokeh/io/webdriver.py
  +++ b/bokeh/io/webdriver.py
  @@ -25,8 +25,9 @@
   
   # Standard library imports
   import atexit
  -import shutil
  -from os.path import devnull
  +import os
  +from os.path import devnull, dirname, isfile, join
  +from shutil import which
   from typing import List, Optional
   
   # External imports
  @@ -53,11 +54,38 @@
   #-----------------------------------------------------------------------------
   
   def create_firefox_webdriver() -> WebDriver:
  +    firefox = which("firefox")
  +    if firefox is None:
  +        raise RuntimeError("firefox is not installed or not present on PATH")
  +
  +    geckodriver = which("geckodriver")
  +    if geckodriver is None:
  +        raise RuntimeError("geckodriver is not installed or not present on PATH")
  +
  +    firefox_paths = [
  +        join(dirname(firefox), "FirefoxApp", "firefox"),
  +        join(dirname(firefox), "FirefoxApp", "Contents", "MacOS", "firefox"),
  +    ]
  +
  +    for firefox_path in firefox_paths:
  +        if _is_executable(firefox_path):
  +            binary_path = firefox_path
  +            break
  +    else:
  +        binary_path = firefox
  +
       from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
  -    binary = FirefoxBinary(_detect("firefox"))
  +    binary = FirefoxBinary(binary_path)
  +
       options = webdriver.firefox.options.Options()
       options.add_argument("--headless")
  -    return webdriver.Firefox(firefox_binary=binary, options=options, service_log_path=devnull)
  +
  +    return webdriver.Firefox(
  +        options=options,
  +        firefox_binary=binary,
  +        executable_path=geckodriver,
  +        service_log_path=devnull,
  +    )
   
   def create_chromium_webdriver() -> WebDriver:
       options = webdriver.chrome.options.Options()
  @@ -71,8 +99,8 @@
   # Private API
   #-----------------------------------------------------------------------------
   
  -def _detect(executable: str) -> Optional[str]:
  -    return shutil.which(executable)
  +def _is_executable(path: str) -> bool:
  +    return isfile(path) and os.access(path, os.X_OK)
   
   def _try_create_firefox_webdriver() -> Optional[WebDriver]:
       try:
  
verification_info: {"golden_diff": "diff --git a/bokeh/io/webdriver.py b/bokeh/io/webdriver.py\n--- a/bokeh/io/webdriver.py\n+++ b/bokeh/io/webdriver.py\n@@ -25,8 +25,9 @@\n \n # Standard library imports\n import atexit\n-import shutil\n-from os.path import devnull\n+import os\n+from os.path import devnull, dirname, isfile, join\n+from shutil import which\n from typing import List, Optional\n \n # External imports\n@@ -53,11 +54,38 @@\n #-----------------------------------------------------------------------------\n \n def create_firefox_webdriver() -> WebDriver:\n+    firefox = which(\"firefox\")\n+    if firefox is None:\n+        raise RuntimeError(\"firefox is not installed or not present on PATH\")\n+\n+    geckodriver = which(\"geckodriver\")\n+    if geckodriver is None:\n+        raise RuntimeError(\"geckodriver is not installed or not present on PATH\")\n+\n+    firefox_paths = [\n+        join(dirname(firefox), \"FirefoxApp\", \"firefox\"),\n+        join(dirname(firefox), \"FirefoxApp\", \"Contents\", \"MacOS\", \"firefox\"),\n+    ]\n+\n+    for firefox_path in firefox_paths:\n+        if _is_executable(firefox_path):\n+            binary_path = firefox_path\n+            break\n+    else:\n+        binary_path = firefox\n+\n     from selenium.webdriver.firefox.firefox_binary import FirefoxBinary\n-    binary = FirefoxBinary(_detect(\"firefox\"))\n+    binary = FirefoxBinary(binary_path)\n+\n     options = webdriver.firefox.options.Options()\n     options.add_argument(\"--headless\")\n-    return webdriver.Firefox(firefox_binary=binary, options=options, service_log_path=devnull)\n+\n+    return webdriver.Firefox(\n+        options=options,\n+        firefox_binary=binary,\n+        executable_path=geckodriver,\n+        service_log_path=devnull,\n+    )\n \n def create_chromium_webdriver() -> WebDriver:\n     options = webdriver.chrome.options.Options()\n@@ -71,8 +99,8 @@\n # Private API\n #-----------------------------------------------------------------------------\n \n-def _detect(executable: str) -> Optional[str]:\n-    return shutil.which(executable)\n+def _is_executable(path: str) -> bool:\n+    return isfile(path) and os.access(path, os.X_OK)\n \n def _try_create_firefox_webdriver() -> Optional[WebDriver]:\n     try:\n", "issue": "webdriver failing to find installed firefox/geckodriver\nCan't get export examples running on binder even though everything is installed and on the path:\r\n\r\n<img width=\"1130\" alt=\"Screen Shot 2020-06-02 at 8 23 16 PM\" src=\"https://user-images.githubusercontent.com/1078448/83592297-e9459c80-a50e-11ea-90d1-7189fcc93af0.png\">\r\n\r\n\n", "before_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2012 - 2020, Anaconda, Inc., and Bokeh Contributors.\n# All rights reserved.\n#\n# The full license is in the file LICENSE.txt, distributed with this software.\n#-----------------------------------------------------------------------------\n'''\n\n'''\n\n#-----------------------------------------------------------------------------\n# Boilerplate\n#-----------------------------------------------------------------------------\nimport logging # isort:skip\nlog = logging.getLogger(__name__)\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n\nfrom ..util.dependencies import import_required # isort:skip\nimport_required(\"selenium.webdriver\",\n                \"To use bokeh.io image export functions you need selenium \"\n                \"('conda install selenium' or 'pip install selenium')\")\n\n# Standard library imports\nimport atexit\nimport shutil\nfrom os.path import devnull\nfrom typing import List, Optional\n\n# External imports\nfrom selenium import webdriver\nfrom selenium.webdriver.remote.webdriver import WebDriver\nfrom typing_extensions import Literal\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\nDriverKind = Literal[\"firefox\", \"chromium\"]\n\n__all__ = (\n    'webdriver_control',\n)\n\n#-----------------------------------------------------------------------------\n# General API\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Dev API\n#-----------------------------------------------------------------------------\n\ndef create_firefox_webdriver() -> WebDriver:\n    from selenium.webdriver.firefox.firefox_binary import FirefoxBinary\n    binary = FirefoxBinary(_detect(\"firefox\"))\n    options = webdriver.firefox.options.Options()\n    options.add_argument(\"--headless\")\n    return webdriver.Firefox(firefox_binary=binary, options=options, service_log_path=devnull)\n\ndef create_chromium_webdriver() -> WebDriver:\n    options = webdriver.chrome.options.Options()\n    options.add_argument(\"--headless\")\n    options.add_argument(\"--hide-scrollbars\")\n    options.add_argument(\"--force-device-scale-factor=1\")\n    options.add_argument(\"--force-color-profile=srgb\")\n    return webdriver.Chrome(options=options)\n\n#-----------------------------------------------------------------------------\n# Private API\n#-----------------------------------------------------------------------------\n\ndef _detect(executable: str) -> Optional[str]:\n    return shutil.which(executable)\n\ndef _try_create_firefox_webdriver() -> Optional[WebDriver]:\n    try:\n        return create_firefox_webdriver()\n    except Exception:\n        return None\n\ndef _try_create_chromium_webdriver() -> Optional[WebDriver]:\n    try:\n        return create_chromium_webdriver()\n    except Exception:\n        return None\n\nclass _WebdriverState:\n    '''\n\n    '''\n\n    reuse: bool\n    kind: Optional[DriverKind]\n\n    current: Optional[WebDriver]\n    _drivers: List[WebDriver]\n\n    def __init__(self, *, kind: Optional[DriverKind] = None, reuse: bool = True):\n        self.kind = kind\n        self.reuse = reuse\n        self.current = None\n        self._drivers = set()\n\n    def terminate(self, driver: WebDriver) -> None:\n        self._drivers.remove(driver)\n        driver.quit()\n\n    def reset(self) -> None:\n        if self.current is not None:\n            self.terminate(self.current)\n            self.current = None\n\n    def get(self) -> WebDriver:\n        if not self.reuse or self.current is None:\n            self.reset()\n            self.current = self.create()\n        return self.current\n\n    def create(self, kind: Optional[DriverKind] = None) -> WebDriver:\n        driver = self._create(kind)\n        self._drivers.add(driver)\n        return driver\n\n    def _create(self, kind: Optional[DriverKind]) -> WebDriver:\n        driver_kind = kind or self.kind\n\n        if driver_kind is None:\n            driver = _try_create_chromium_webdriver()\n            if driver is not None:\n                self.kind = \"chromium\"\n                return driver\n\n            driver = _try_create_firefox_webdriver()\n            if driver is not None:\n                self.kind = \"firefox\"\n                return driver\n\n            raise RuntimeError(\"Neither firefox and geckodriver nor a variant of chromium browser and \" \\\n                               \"chromedriver are available on system PATH. You can install the former \" \\\n                               \"with 'conda install -c conda-forge firefox geckodriver'.\")\n        elif driver_kind == \"chromium\":\n            return create_chromium_webdriver()\n        elif driver_kind == \"firefox\":\n            return create_firefox_webdriver()\n        else:\n            raise ValueError(f\"'{driver_kind}' is not a recognized webdriver kind\")\n\n    def cleanup(self) -> None:\n        self.reset()\n        for driver in list(self._drivers):\n            self.terminate(driver)\n\n#-----------------------------------------------------------------------------\n# Code\n#-----------------------------------------------------------------------------\n\nwebdriver_control = _WebdriverState()\n\natexit.register(lambda: webdriver_control.cleanup())\n", "path": "bokeh/io/webdriver.py"}]}