prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Windows phantomjs not killed on selenium termination
  I reinstalled a fresh python environment on windows with python 3.7 and pythonenv
  I use only pip for package dependencies
  When I tried to run tests some failed because temp files were locked.
  <img width="726" alt="win32error" src="https://user-images.githubusercontent.com/18531147/54091987-214f4580-4387-11e9-9584-6a117a356ad2.png">
  <img width="257" alt="test_failures" src="https://user-images.githubusercontent.com/18531147/54091989-24e2cc80-4387-11e9-9c42-3573dabd1813.PNG">
  
  
  When driver terminate phantomjs is not correctly killed:
  <img width="294" alt="proc_pantomjs" src="https://user-images.githubusercontent.com/18531147/54092002-45128b80-4387-11e9-9967-bf74b1e41bd7.PNG">
  
  
  </issue>
  <code>
  [start of bokeh/io/webdriver.py]
  1 #-----------------------------------------------------------------------------
  2 # Copyright (c) 2012 - 2019, 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 from __future__ import absolute_import, division, print_function, unicode_literals
  15 
  16 import logging
  17 log = logging.getLogger(__name__)
  18 
  19 #-----------------------------------------------------------------------------
  20 # Imports
  21 #-----------------------------------------------------------------------------
  22 
  23 # Standard library imports
  24 import atexit
  25 import signal
  26 import warnings
  27 from os.path import devnull
  28 
  29 # External imports
  30 
  31 # Bokeh imports
  32 from ..util.dependencies import import_required, detect_phantomjs
  33 
  34 #-----------------------------------------------------------------------------
  35 # Globals and constants
  36 #-----------------------------------------------------------------------------
  37 
  38 __all__ = (
  39     'create_phantomjs_webdriver',
  40     'terminate_webdriver',
  41     'webdriver_control',
  42 )
  43 
  44 #-----------------------------------------------------------------------------
  45 # General API
  46 #-----------------------------------------------------------------------------
  47 
  48 #-----------------------------------------------------------------------------
  49 # Dev API
  50 #-----------------------------------------------------------------------------
  51 
  52 def create_phantomjs_webdriver():
  53     with warnings.catch_warnings():
  54         warnings.filterwarnings("ignore", ".*", UserWarning, "selenium.webdriver.phantomjs.webdriver")
  55 
  56         webdriver = import_required('selenium.webdriver',
  57                                     'To use bokeh.io image export functions you need selenium ' +
  58                                     '("conda install -c bokeh selenium" or "pip install selenium")')
  59 
  60         phantomjs_path = detect_phantomjs()
  61         return webdriver.PhantomJS(executable_path=phantomjs_path, service_log_path=devnull)
  62 
  63 def terminate_webdriver(driver):
  64     if driver.name == "phantomjs":
  65         # https://github.com/seleniumhq/selenium/issues/767
  66         if driver.service.process:
  67             driver.service.process.send_signal(signal.SIGTERM)
  68 
  69     try:
  70         driver.quit()
  71     except (IOError, OSError): # IOError for Python 2.7
  72         pass
  73 
  74 #-----------------------------------------------------------------------------
  75 # Private API
  76 #-----------------------------------------------------------------------------
  77 
  78 class _WebdriverState(object):
  79     '''
  80 
  81     '''
  82 
  83     def __init__(self, reuse=True, kind="phantomjs"):
  84         self.reuse = reuse
  85         self.kind = kind
  86         self.current = None
  87 
  88     def reset(self):
  89         if self.current is not None:
  90             terminate_webdriver(self.current)
  91             self.current = None
  92 
  93     def get(self):
  94         if not self.reuse or self.current is None:
  95             if self.current is not None:
  96                 terminate_webdriver(self.current)
  97             self.current = self.create()
  98         return self.current
  99 
  100     def create(self):
  101         if self.kind == "phantomjs":
  102             return create_phantomjs_webdriver()
  103         raise ValueError("Unknown webdriver kind %r" % self.kind)
  104 
  105     @property
  106     def reuse(self):
  107         return self._reuse
  108 
  109     @reuse.setter
  110     def reuse(self, value):
  111         self._reuse = value
  112 
  113     @property
  114     def kind(self):
  115         return self._kind
  116 
  117     @kind.setter
  118     def kind(self, value):
  119         # TODO (bev) enum/value check when more are added
  120         self._kind = value
  121 
  122 #-----------------------------------------------------------------------------
  123 # Code
  124 #-----------------------------------------------------------------------------
  125 
  126 
  127 webdriver_control = _WebdriverState()
  128 
  129 atexit.register(lambda: webdriver_control.reset())
  130
  [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
  @@ -21,6 +21,7 @@
   #-----------------------------------------------------------------------------
   
   # Standard library imports
  +import sys
   import atexit
   import signal
   import warnings
  @@ -29,7 +30,7 @@
   # External imports
   
   # Bokeh imports
  -from ..util.dependencies import import_required, detect_phantomjs
  +from ..util.dependencies import import_required, detect_phantomjs, import_optional
   
   #-----------------------------------------------------------------------------
   # Globals and constants
  @@ -49,6 +50,20 @@
   # Dev API
   #-----------------------------------------------------------------------------
   
  +
  +def kill_proc_tree(pid, including_parent=True):
  +    psutil = import_optional('psutil')
  +    if psutil is not None:
  +        parent = psutil.Process(pid)
  +        children = parent.children(recursive=True)
  +        for child in children:
  +            child.kill()
  +        psutil.wait_procs(children)
  +        if including_parent:
  +            parent.kill()
  +            parent.wait(5)
  +
  +
   def create_phantomjs_webdriver():
       with warnings.catch_warnings():
           warnings.filterwarnings("ignore", ".*", UserWarning, "selenium.webdriver.phantomjs.webdriver")
  @@ -60,21 +75,25 @@
           phantomjs_path = detect_phantomjs()
           return webdriver.PhantomJS(executable_path=phantomjs_path, service_log_path=devnull)
   
  +
   def terminate_webdriver(driver):
       if driver.name == "phantomjs":
           # https://github.com/seleniumhq/selenium/issues/767
           if driver.service.process:
  +            if sys.platform == 'win32':
  +                kill_proc_tree(driver.service.process.pid, including_parent=False)
               driver.service.process.send_signal(signal.SIGTERM)
   
       try:
           driver.quit()
  -    except (IOError, OSError): # IOError for Python 2.7
  +    except (IOError, OSError):  # IOError for Python 2.7
           pass
   
   #-----------------------------------------------------------------------------
   # Private API
   #-----------------------------------------------------------------------------
   
  +
   class _WebdriverState(object):
       '''
  
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@@ -21,6 +21,7 @@\n #-----------------------------------------------------------------------------\n \n # Standard library imports\n+import sys\n import atexit\n import signal\n import warnings\n@@ -29,7 +30,7 @@\n # External imports\n \n # Bokeh imports\n-from ..util.dependencies import import_required, detect_phantomjs\n+from ..util.dependencies import import_required, detect_phantomjs, import_optional\n \n #-----------------------------------------------------------------------------\n # Globals and constants\n@@ -49,6 +50,20 @@\n # Dev API\n #-----------------------------------------------------------------------------\n \n+\n+def kill_proc_tree(pid, including_parent=True):\n+    psutil = import_optional('psutil')\n+    if psutil is not None:\n+        parent = psutil.Process(pid)\n+        children = parent.children(recursive=True)\n+        for child in children:\n+            child.kill()\n+        psutil.wait_procs(children)\n+        if including_parent:\n+            parent.kill()\n+            parent.wait(5)\n+\n+\n def create_phantomjs_webdriver():\n     with warnings.catch_warnings():\n         warnings.filterwarnings(\"ignore\", \".*\", UserWarning, \"selenium.webdriver.phantomjs.webdriver\")\n@@ -60,21 +75,25 @@\n         phantomjs_path = detect_phantomjs()\n         return webdriver.PhantomJS(executable_path=phantomjs_path, service_log_path=devnull)\n \n+\n def terminate_webdriver(driver):\n     if driver.name == \"phantomjs\":\n         # https://github.com/seleniumhq/selenium/issues/767\n         if driver.service.process:\n+            if sys.platform == 'win32':\n+                kill_proc_tree(driver.service.process.pid, including_parent=False)\n             driver.service.process.send_signal(signal.SIGTERM)\n \n     try:\n         driver.quit()\n-    except (IOError, OSError): # IOError for Python 2.7\n+    except (IOError, OSError):  # IOError for Python 2.7\n         pass\n \n #-----------------------------------------------------------------------------\n # Private API\n #-----------------------------------------------------------------------------\n \n+\n class _WebdriverState(object):\n     '''\n", "issue": "Windows phantomjs not killed on selenium termination\nI reinstalled a fresh python environment on windows with python 3.7 and pythonenv\r\nI use only pip for package dependencies\r\nWhen I tried to run tests some failed because temp files were locked.\r\n<img width=\"726\" alt=\"win32error\" src=\"https://user-images.githubusercontent.com/18531147/54091987-214f4580-4387-11e9-9584-6a117a356ad2.png\">\r\n<img width=\"257\" alt=\"test_failures\" src=\"https://user-images.githubusercontent.com/18531147/54091989-24e2cc80-4387-11e9-9c42-3573dabd1813.PNG\">\r\n\r\n\r\nWhen driver terminate phantomjs is not correctly killed:\r\n<img width=\"294\" alt=\"proc_pantomjs\" src=\"https://user-images.githubusercontent.com/18531147/54092002-45128b80-4387-11e9-9967-bf74b1e41bd7.PNG\">\r\n\n", "before_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2012 - 2019, 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#-----------------------------------------------------------------------------\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\nlog = logging.getLogger(__name__)\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n\n# Standard library imports\nimport atexit\nimport signal\nimport warnings\nfrom os.path import devnull\n\n# External imports\n\n# Bokeh imports\nfrom ..util.dependencies import import_required, detect_phantomjs\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\n__all__ = (\n    'create_phantomjs_webdriver',\n    'terminate_webdriver',\n    'webdriver_control',\n)\n\n#-----------------------------------------------------------------------------\n# General API\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Dev API\n#-----------------------------------------------------------------------------\n\ndef create_phantomjs_webdriver():\n    with warnings.catch_warnings():\n        warnings.filterwarnings(\"ignore\", \".*\", UserWarning, \"selenium.webdriver.phantomjs.webdriver\")\n\n        webdriver = import_required('selenium.webdriver',\n                                    'To use bokeh.io image export functions you need selenium ' +\n                                    '(\"conda install -c bokeh selenium\" or \"pip install selenium\")')\n\n        phantomjs_path = detect_phantomjs()\n        return webdriver.PhantomJS(executable_path=phantomjs_path, service_log_path=devnull)\n\ndef terminate_webdriver(driver):\n    if driver.name == \"phantomjs\":\n        # https://github.com/seleniumhq/selenium/issues/767\n        if driver.service.process:\n            driver.service.process.send_signal(signal.SIGTERM)\n\n    try:\n        driver.quit()\n    except (IOError, OSError): # IOError for Python 2.7\n        pass\n\n#-----------------------------------------------------------------------------\n# Private API\n#-----------------------------------------------------------------------------\n\nclass _WebdriverState(object):\n    '''\n\n    '''\n\n    def __init__(self, reuse=True, kind=\"phantomjs\"):\n        self.reuse = reuse\n        self.kind = kind\n        self.current = None\n\n    def reset(self):\n        if self.current is not None:\n            terminate_webdriver(self.current)\n            self.current = None\n\n    def get(self):\n        if not self.reuse or self.current is None:\n            if self.current is not None:\n                terminate_webdriver(self.current)\n            self.current = self.create()\n        return self.current\n\n    def create(self):\n        if self.kind == \"phantomjs\":\n            return create_phantomjs_webdriver()\n        raise ValueError(\"Unknown webdriver kind %r\" % self.kind)\n\n    @property\n    def reuse(self):\n        return self._reuse\n\n    @reuse.setter\n    def reuse(self, value):\n        self._reuse = value\n\n    @property\n    def kind(self):\n        return self._kind\n\n    @kind.setter\n    def kind(self, value):\n        # TODO (bev) enum/value check when more are added\n        self._kind = value\n\n#-----------------------------------------------------------------------------\n# Code\n#-----------------------------------------------------------------------------\n\n\nwebdriver_control = _WebdriverState()\n\natexit.register(lambda: webdriver_control.reset())\n", "path": "bokeh/io/webdriver.py"}]}