prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Two renderers with same name
  Working with a fresh Anaconda installation of Jupyter 3.6.  Followed Altair Notebook installation instructions.  Basic example (flower petals scatter plot) rendered but with JSON underneath graph (issue #634).  I thought this might be due to having selected the wrong renderer., so I went to list the available renderers. 
  
  `alt.renderers.names()`
  returns
  `['default', 'json', 'notebook', 'notebook']`
  
  Secretly hoping the second `notebook` renderer solves  #634.  In any case, I think you shouldn't be able to have two renderers with the same name.  
  
  (hs teacher, maybe I'm missing something)
  
  </issue>
  <code>
  [start of altair/utils/plugin_registry.py]
  1 from typing import Callable, Generic, List, TypeVar, Union, cast
  2 
  3 import entrypoints
  4 
  5 
  6 PluginType = TypeVar('PluginType')
  7 
  8 
  9 class PluginRegistry(Generic[PluginType]):
  10     """A registry for plugins.
  11 
  12     This is a plugin registry that allows plugins to be loaded/registered
  13     in two ways:
  14 
  15     1. Through an explicit call to ``.register(name, value)``.
  16     2. By looking for other Python packages that are installed and provide
  17        a setuptools entry point group.
  18 
  19     When you create an instance of this class, provide the name of the
  20     entry point group to use::
  21 
  22         reg = PluginRegister('my_entrypoint_group')
  23 
  24     """
  25     # this is a mapping of name to error message to allow custom error messages
  26     # in case an entrypoint is not found
  27     entrypoint_err_messages = {}
  28 
  29     def __init__(self, entry_point_group: str = '', plugin_type=object) -> None:
  30         """Create a PluginRegistry for a named entry point group.
  31 
  32         Parameters
  33         ==========
  34         entry_point_group: str
  35             The name of the entry point group.
  36         plugin_type: object
  37             A type that will optionally be used for runtime type checking of
  38             loaded plugins using isinstance.
  39         """
  40         self.entry_point_group = entry_point_group
  41         self.plugin_type = plugin_type
  42         self._active = None     # type: None
  43         self._active_name = ''  # type: str
  44         self._plugins = {}      # type: dict
  45         self._options = {}      # type: dict
  46 
  47     def register(self, name: str, value: Union[PluginType,None]) -> PluginType:
  48         """Register a plugin by name and value.
  49 
  50         This method is used for explicit registration of a plugin and shouldn't be
  51         used to manage entry point managed plugins, which are auto-loaded.
  52 
  53         Parameters
  54         ==========
  55         name: str
  56             The name of the plugin.
  57         value: PluginType or None
  58             The actual plugin object to register or None to unregister that plugin.
  59 
  60         Returns
  61         =======
  62         plugin: PluginType
  63             The plugin that was registered or unregistered.
  64         """
  65         if value is None and name in self._plugins:
  66             return self._plugins.pop(name)
  67         else:
  68             assert isinstance(value, self.plugin_type)
  69             self._plugins[name] = value
  70             return value
  71 
  72 
  73     def names(self) -> List[str]:
  74         """List the names of the registered and entry points plugins."""
  75         exts = list(self._plugins.keys())
  76         more_exts = [ep.name for ep in entrypoints.get_group_all(self.entry_point_group)]
  77         exts.extend(more_exts)
  78         return exts
  79 
  80 
  81     def enable(self, name: str) -> None:
  82         """Enable a plugin by name."""
  83         if name not in self._plugins:
  84             try:
  85                 ep = entrypoints.get_single(self.entry_point_group, name)
  86             except entrypoints.NoSuchEntryPoint as err:
  87                 if name in self.entrypoint_err_messages:
  88                     raise ValueError(self.entrypoint_err_messages[name])
  89                 else:
  90                     raise
  91             value = cast(PluginType, ep.load())
  92             assert isinstance(value, self.plugin_type)
  93             self.register(name, value)
  94         self._active_name = name
  95         self._active = self._plugins[name]
  96 
  97     @property
  98     def active(self) -> str:
  99         """Return the name of the currently active plugin"""
  100         return self._active_name
  101 
  102     def get(self) -> PluginType:
  103         """Return the currently active plugin."""
  104         return self._active
  105 
  106     def __repr__(self) -> str:
  107         return ("{0}(active={1!r}, registered={2!r})"
  108                 "".format(self.__class__.__name__,
  109                           self._active_name,
  110                           list(self.names())))
  111
  [end of altair/utils/plugin_registry.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/altair/utils/plugin_registry.py b/altair/utils/plugin_registry.py
  --- a/altair/utils/plugin_registry.py
  +++ b/altair/utils/plugin_registry.py
  @@ -75,7 +75,7 @@
           exts = list(self._plugins.keys())
           more_exts = [ep.name for ep in entrypoints.get_group_all(self.entry_point_group)]
           exts.extend(more_exts)
  -        return exts
  +        return sorted(set(exts))
   
   
       def enable(self, name: str) -> None:
  
verification_info: {"golden_diff": "diff --git a/altair/utils/plugin_registry.py b/altair/utils/plugin_registry.py\n--- a/altair/utils/plugin_registry.py\n+++ b/altair/utils/plugin_registry.py\n@@ -75,7 +75,7 @@\n         exts = list(self._plugins.keys())\n         more_exts = [ep.name for ep in entrypoints.get_group_all(self.entry_point_group)]\n         exts.extend(more_exts)\n-        return exts\n+        return sorted(set(exts))\n \n \n     def enable(self, name: str) -> None:\n", "issue": "Two renderers with same name\nWorking with a fresh Anaconda installation of Jupyter 3.6.  Followed Altair Notebook installation instructions.  Basic example (flower petals scatter plot) rendered but with JSON underneath graph (issue #634).  I thought this might be due to having selected the wrong renderer., so I went to list the available renderers. \r\n\r\n`alt.renderers.names()`\r\nreturns\r\n`['default', 'json', 'notebook', 'notebook']`\r\n\r\nSecretly hoping the second `notebook` renderer solves  #634.  In any case, I think you shouldn't be able to have two renderers with the same name.  \r\n\r\n(hs teacher, maybe I'm missing something)\n", "before_files": [{"content": "from typing import Callable, Generic, List, TypeVar, Union, cast\n\nimport entrypoints\n\n\nPluginType = TypeVar('PluginType')\n\n\nclass PluginRegistry(Generic[PluginType]):\n    \"\"\"A registry for plugins.\n\n    This is a plugin registry that allows plugins to be loaded/registered\n    in two ways:\n\n    1. Through an explicit call to ``.register(name, value)``.\n    2. By looking for other Python packages that are installed and provide\n       a setuptools entry point group.\n\n    When you create an instance of this class, provide the name of the\n    entry point group to use::\n\n        reg = PluginRegister('my_entrypoint_group')\n\n    \"\"\"\n    # this is a mapping of name to error message to allow custom error messages\n    # in case an entrypoint is not found\n    entrypoint_err_messages = {}\n\n    def __init__(self, entry_point_group: str = '', plugin_type=object) -> None:\n        \"\"\"Create a PluginRegistry for a named entry point group.\n\n        Parameters\n        ==========\n        entry_point_group: str\n            The name of the entry point group.\n        plugin_type: object\n            A type that will optionally be used for runtime type checking of\n            loaded plugins using isinstance.\n        \"\"\"\n        self.entry_point_group = entry_point_group\n        self.plugin_type = plugin_type\n        self._active = None     # type: None\n        self._active_name = ''  # type: str\n        self._plugins = {}      # type: dict\n        self._options = {}      # type: dict\n\n    def register(self, name: str, value: Union[PluginType,None]) -> PluginType:\n        \"\"\"Register a plugin by name and value.\n\n        This method is used for explicit registration of a plugin and shouldn't be\n        used to manage entry point managed plugins, which are auto-loaded.\n\n        Parameters\n        ==========\n        name: str\n            The name of the plugin.\n        value: PluginType or None\n            The actual plugin object to register or None to unregister that plugin.\n\n        Returns\n        =======\n        plugin: PluginType\n            The plugin that was registered or unregistered.\n        \"\"\"\n        if value is None and name in self._plugins:\n            return self._plugins.pop(name)\n        else:\n            assert isinstance(value, self.plugin_type)\n            self._plugins[name] = value\n            return value\n\n\n    def names(self) -> List[str]:\n        \"\"\"List the names of the registered and entry points plugins.\"\"\"\n        exts = list(self._plugins.keys())\n        more_exts = [ep.name for ep in entrypoints.get_group_all(self.entry_point_group)]\n        exts.extend(more_exts)\n        return exts\n\n\n    def enable(self, name: str) -> None:\n        \"\"\"Enable a plugin by name.\"\"\"\n        if name not in self._plugins:\n            try:\n                ep = entrypoints.get_single(self.entry_point_group, name)\n            except entrypoints.NoSuchEntryPoint as err:\n                if name in self.entrypoint_err_messages:\n                    raise ValueError(self.entrypoint_err_messages[name])\n                else:\n                    raise\n            value = cast(PluginType, ep.load())\n            assert isinstance(value, self.plugin_type)\n            self.register(name, value)\n        self._active_name = name\n        self._active = self._plugins[name]\n\n    @property\n    def active(self) -> str:\n        \"\"\"Return the name of the currently active plugin\"\"\"\n        return self._active_name\n\n    def get(self) -> PluginType:\n        \"\"\"Return the currently active plugin.\"\"\"\n        return self._active\n\n    def __repr__(self) -> str:\n        return (\"{0}(active={1!r}, registered={2!r})\"\n                \"\".format(self.__class__.__name__,\n                          self._active_name,\n                          list(self.names())))\n", "path": "altair/utils/plugin_registry.py"}]}