We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `altair/utils/plugin_registry.py`
Content:
```
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 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()

```

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:

{"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"}], "after_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 sorted(set(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"}]}