We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Maintain coherence between keycomments and the metadict
See #2748 

This is probably best implemented by adding the functionality to our `MetaDict` object or something, so that we don't have to do it manually everywhere.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sunpy/util/metadata.py`
Content:
```
1 """
2 This module provides a generalized dictionary class that deals with header
3 parsing and normalization.
4 """
5 from collections import OrderedDict
6 
7 __all__ = ['MetaDict']
8 
9 
10 class MetaDict(OrderedDict):
11     """
12     A class to hold metadata associated with a `sunpy.map.Map
13     <sunpy.map.map_factory.MapFactory.__call__>` derivative.
14 
15     This class handles everything in lower case. This allows case
16     insensitive indexing.
17     """
18 
19     def __init__(self, *args):
20         """
21         Creates a new MapHeader instance.
22         """
23         # Store all keys as upper-case to allow for case-insensitive indexing
24         # OrderedDict can be instantiated from a list of lists or a tuple of tuples
25         tags = dict()
26         if args:
27             args = list(args)
28             adict = args[0]
29             if isinstance(adict, list) or isinstance(adict, tuple):
30                 tags = OrderedDict((k.upper(), v) for k, v in adict)
31             elif isinstance(adict, dict):
32                 tags = OrderedDict((k.upper(), v) for k, v in adict.items())
33             else:
34                 raise TypeError("Can not create a MetaDict from this type input")
35             args[0] = tags
36 
37         super().__init__(*args)
38 
39     def __contains__(self, key):
40         """
41         Override ``__contains__``.
42         """
43         return OrderedDict.__contains__(self, key.lower())
44 
45     def __getitem__(self, key):
46         """
47         Override ``[]`` indexing.
48         """
49         return OrderedDict.__getitem__(self, key.lower())
50 
51     def __setitem__(self, key, value):
52         """
53         Override ``[]`` indexing.
54         """
55         return OrderedDict.__setitem__(self, key.lower(), value)
56 
57     def get(self, key, default=None):
58         """
59         Override ``.get()`` indexing.
60         """
61         return OrderedDict.get(self, key.lower(), default)
62 
63     def has_key(self, key):
64         """
65         Override ``.has_key()`` to perform case-insensitively.
66         """
67         return key.lower() in self
68 
69     def pop(self, key, default=None):
70         """
71         Override ``.pop()`` to perform case-insensitively.
72         """
73         return OrderedDict.pop(self, key.lower(), default)
74 
75     def update(self, d2):
76         """
77         Override ``.update()`` to perform case-insensitively.
78         """
79         return OrderedDict.update(self, OrderedDict((k.lower(), v) for k, v in d2.items()))
80 
81     def setdefault(self, key, default=None):
82         """
83         Override ``.setdefault()`` to perform case-insensitively.
84         """
85         return OrderedDict.setdefault(self, key.lower(), default)
86
```
--- 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/sunpy/util/metadata.py b/sunpy/util/metadata.py
--- a/sunpy/util/metadata.py
+++ b/sunpy/util/metadata.py
@@ -1,6 +1,6 @@
 """
 This module provides a generalized dictionary class that deals with header
-parsing and normalization.
+parsing, normalization, and maintaining coherence between keys and keycomments.
 """
 from collections import OrderedDict
 
@@ -14,28 +14,67 @@
 
     This class handles everything in lower case. This allows case
     insensitive indexing.
+
+    If the key 'keycomments' exists, its value must be a dictionary mapping
+    keys in the `MetaDict` to their comments. The casing of keys in the
+    keycomments dictionary is not significant. If a key is removed from the
+    `MetaDict`, it will also be removed from the keycomments dictionary.
+    Additionally, any extraneous keycomments will be removed when the
+    `MetaDict` is instantiated.
     """
 
     def __init__(self, *args):
         """
-        Creates a new MapHeader instance.
+        Creates a new MetaDict instance.
         """
-        # Store all keys as upper-case to allow for case-insensitive indexing
+        # Store all keys as lower-case to allow for case-insensitive indexing
         # OrderedDict can be instantiated from a list of lists or a tuple of tuples
         tags = dict()
         if args:
             args = list(args)
             adict = args[0]
             if isinstance(adict, list) or isinstance(adict, tuple):
-                tags = OrderedDict((k.upper(), v) for k, v in adict)
+                tags = OrderedDict((k.lower(), v) for k, v in adict)
             elif isinstance(adict, dict):
-                tags = OrderedDict((k.upper(), v) for k, v in adict.items())
+                tags = OrderedDict((k.lower(), v) for k, v in adict.items())
             else:
                 raise TypeError("Can not create a MetaDict from this type input")
             args[0] = tags
 
         super().__init__(*args)
 
+        # Use `copy=True` to avoid mutating the caller's keycomments
+        # dictionary (if they provided one).
+        self._prune_keycomments(copy=True)
+
+    def _prune_keycomments(self, copy=False):
+        """
+        Remove keycomments for keys that are not contained in the MetaDict.
+
+        Parameters
+        ----------
+        copy : `bool`, optional
+            Make a copy of the current keycomments dict before removing keys.
+        """
+        if 'keycomments' not in self:
+            return
+
+        keycomments = self['keycomments']
+
+        if not isinstance(keycomments, dict):
+            raise TypeError(
+                "'keycomments' key must have a value of type `dict`. Found "
+                "the following type: %r" % type(keycomments))
+
+        if copy:
+            keycomments = keycomments.copy()
+
+        for key in list(keycomments.keys()):
+            if key not in self:
+                del keycomments[key]
+
+        self['keycomments'] = keycomments
+
     def __contains__(self, key):
         """
         Override ``__contains__``.
@@ -54,6 +93,15 @@
         """
         return OrderedDict.__setitem__(self, key.lower(), value)
 
+    # Note: `OrderedDict.popitem()` does not need to be overridden to prune
+    # keycomments because it calls `__delitem__` internally.
+    def __delitem__(self, key):
+        """
+        Override ``del dict[key]`` key deletion.
+        """
+        OrderedDict.__delitem__(self, key.lower())
+        self._prune_keycomments()
+
     def get(self, key, default=None):
         """
         Override ``.get()`` indexing.
@@ -70,7 +118,11 @@
         """
         Override ``.pop()`` to perform case-insensitively.
         """
-        return OrderedDict.pop(self, key.lower(), default)
+        has_key = key in self
+        result = OrderedDict.pop(self, key.lower(), default)
+        if has_key:
+            self._prune_keycomments()
+        return result
 
     def update(self, d2):
         """

{"golden_diff": "diff --git a/sunpy/util/metadata.py b/sunpy/util/metadata.py\n--- a/sunpy/util/metadata.py\n+++ b/sunpy/util/metadata.py\n@@ -1,6 +1,6 @@\n \"\"\"\n This module provides a generalized dictionary class that deals with header\n-parsing and normalization.\n+parsing, normalization, and maintaining coherence between keys and keycomments.\n \"\"\"\n from collections import OrderedDict\n \n@@ -14,28 +14,67 @@\n \n     This class handles everything in lower case. This allows case\n     insensitive indexing.\n+\n+    If the key 'keycomments' exists, its value must be a dictionary mapping\n+    keys in the `MetaDict` to their comments. The casing of keys in the\n+    keycomments dictionary is not significant. If a key is removed from the\n+    `MetaDict`, it will also be removed from the keycomments dictionary.\n+    Additionally, any extraneous keycomments will be removed when the\n+    `MetaDict` is instantiated.\n     \"\"\"\n \n     def __init__(self, *args):\n         \"\"\"\n-        Creates a new MapHeader instance.\n+        Creates a new MetaDict instance.\n         \"\"\"\n-        # Store all keys as upper-case to allow for case-insensitive indexing\n+        # Store all keys as lower-case to allow for case-insensitive indexing\n         # OrderedDict can be instantiated from a list of lists or a tuple of tuples\n         tags = dict()\n         if args:\n             args = list(args)\n             adict = args[0]\n             if isinstance(adict, list) or isinstance(adict, tuple):\n-                tags = OrderedDict((k.upper(), v) for k, v in adict)\n+                tags = OrderedDict((k.lower(), v) for k, v in adict)\n             elif isinstance(adict, dict):\n-                tags = OrderedDict((k.upper(), v) for k, v in adict.items())\n+                tags = OrderedDict((k.lower(), v) for k, v in adict.items())\n             else:\n                 raise TypeError(\"Can not create a MetaDict from this type input\")\n             args[0] = tags\n \n         super().__init__(*args)\n \n+        # Use `copy=True` to avoid mutating the caller's keycomments\n+        # dictionary (if they provided one).\n+        self._prune_keycomments(copy=True)\n+\n+    def _prune_keycomments(self, copy=False):\n+        \"\"\"\n+        Remove keycomments for keys that are not contained in the MetaDict.\n+\n+        Parameters\n+        ----------\n+        copy : `bool`, optional\n+            Make a copy of the current keycomments dict before removing keys.\n+        \"\"\"\n+        if 'keycomments' not in self:\n+            return\n+\n+        keycomments = self['keycomments']\n+\n+        if not isinstance(keycomments, dict):\n+            raise TypeError(\n+                \"'keycomments' key must have a value of type `dict`. Found \"\n+                \"the following type: %r\" % type(keycomments))\n+\n+        if copy:\n+            keycomments = keycomments.copy()\n+\n+        for key in list(keycomments.keys()):\n+            if key not in self:\n+                del keycomments[key]\n+\n+        self['keycomments'] = keycomments\n+\n     def __contains__(self, key):\n         \"\"\"\n         Override ``__contains__``.\n@@ -54,6 +93,15 @@\n         \"\"\"\n         return OrderedDict.__setitem__(self, key.lower(), value)\n \n+    # Note: `OrderedDict.popitem()` does not need to be overridden to prune\n+    # keycomments because it calls `__delitem__` internally.\n+    def __delitem__(self, key):\n+        \"\"\"\n+        Override ``del dict[key]`` key deletion.\n+        \"\"\"\n+        OrderedDict.__delitem__(self, key.lower())\n+        self._prune_keycomments()\n+\n     def get(self, key, default=None):\n         \"\"\"\n         Override ``.get()`` indexing.\n@@ -70,7 +118,11 @@\n         \"\"\"\n         Override ``.pop()`` to perform case-insensitively.\n         \"\"\"\n-        return OrderedDict.pop(self, key.lower(), default)\n+        has_key = key in self\n+        result = OrderedDict.pop(self, key.lower(), default)\n+        if has_key:\n+            self._prune_keycomments()\n+        return result\n \n     def update(self, d2):\n         \"\"\"\n", "issue": "Maintain coherence between keycomments and the metadict\nSee #2748 \r\n\r\nThis is probably best implemented by adding the functionality to our `MetaDict` object or something, so that we don't have to do it manually everywhere.\n", "before_files": [{"content": "\"\"\"\nThis module provides a generalized dictionary class that deals with header\nparsing and normalization.\n\"\"\"\nfrom collections import OrderedDict\n\n__all__ = ['MetaDict']\n\n\nclass MetaDict(OrderedDict):\n    \"\"\"\n    A class to hold metadata associated with a `sunpy.map.Map\n    <sunpy.map.map_factory.MapFactory.__call__>` derivative.\n\n    This class handles everything in lower case. This allows case\n    insensitive indexing.\n    \"\"\"\n\n    def __init__(self, *args):\n        \"\"\"\n        Creates a new MapHeader instance.\n        \"\"\"\n        # Store all keys as upper-case to allow for case-insensitive indexing\n        # OrderedDict can be instantiated from a list of lists or a tuple of tuples\n        tags = dict()\n        if args:\n            args = list(args)\n            adict = args[0]\n            if isinstance(adict, list) or isinstance(adict, tuple):\n                tags = OrderedDict((k.upper(), v) for k, v in adict)\n            elif isinstance(adict, dict):\n                tags = OrderedDict((k.upper(), v) for k, v in adict.items())\n            else:\n                raise TypeError(\"Can not create a MetaDict from this type input\")\n            args[0] = tags\n\n        super().__init__(*args)\n\n    def __contains__(self, key):\n        \"\"\"\n        Override ``__contains__``.\n        \"\"\"\n        return OrderedDict.__contains__(self, key.lower())\n\n    def __getitem__(self, key):\n        \"\"\"\n        Override ``[]`` indexing.\n        \"\"\"\n        return OrderedDict.__getitem__(self, key.lower())\n\n    def __setitem__(self, key, value):\n        \"\"\"\n        Override ``[]`` indexing.\n        \"\"\"\n        return OrderedDict.__setitem__(self, key.lower(), value)\n\n    def get(self, key, default=None):\n        \"\"\"\n        Override ``.get()`` indexing.\n        \"\"\"\n        return OrderedDict.get(self, key.lower(), default)\n\n    def has_key(self, key):\n        \"\"\"\n        Override ``.has_key()`` to perform case-insensitively.\n        \"\"\"\n        return key.lower() in self\n\n    def pop(self, key, default=None):\n        \"\"\"\n        Override ``.pop()`` to perform case-insensitively.\n        \"\"\"\n        return OrderedDict.pop(self, key.lower(), default)\n\n    def update(self, d2):\n        \"\"\"\n        Override ``.update()`` to perform case-insensitively.\n        \"\"\"\n        return OrderedDict.update(self, OrderedDict((k.lower(), v) for k, v in d2.items()))\n\n    def setdefault(self, key, default=None):\n        \"\"\"\n        Override ``.setdefault()`` to perform case-insensitively.\n        \"\"\"\n        return OrderedDict.setdefault(self, key.lower(), default)\n", "path": "sunpy/util/metadata.py"}], "after_files": [{"content": "\"\"\"\nThis module provides a generalized dictionary class that deals with header\nparsing, normalization, and maintaining coherence between keys and keycomments.\n\"\"\"\nfrom collections import OrderedDict\n\n__all__ = ['MetaDict']\n\n\nclass MetaDict(OrderedDict):\n    \"\"\"\n    A class to hold metadata associated with a `sunpy.map.Map\n    <sunpy.map.map_factory.MapFactory.__call__>` derivative.\n\n    This class handles everything in lower case. This allows case\n    insensitive indexing.\n\n    If the key 'keycomments' exists, its value must be a dictionary mapping\n    keys in the `MetaDict` to their comments. The casing of keys in the\n    keycomments dictionary is not significant. If a key is removed from the\n    `MetaDict`, it will also be removed from the keycomments dictionary.\n    Additionally, any extraneous keycomments will be removed when the\n    `MetaDict` is instantiated.\n    \"\"\"\n\n    def __init__(self, *args):\n        \"\"\"\n        Creates a new MetaDict instance.\n        \"\"\"\n        # Store all keys as lower-case to allow for case-insensitive indexing\n        # OrderedDict can be instantiated from a list of lists or a tuple of tuples\n        tags = dict()\n        if args:\n            args = list(args)\n            adict = args[0]\n            if isinstance(adict, list) or isinstance(adict, tuple):\n                tags = OrderedDict((k.lower(), v) for k, v in adict)\n            elif isinstance(adict, dict):\n                tags = OrderedDict((k.lower(), v) for k, v in adict.items())\n            else:\n                raise TypeError(\"Can not create a MetaDict from this type input\")\n            args[0] = tags\n\n        super().__init__(*args)\n\n        # Use `copy=True` to avoid mutating the caller's keycomments\n        # dictionary (if they provided one).\n        self._prune_keycomments(copy=True)\n\n    def _prune_keycomments(self, copy=False):\n        \"\"\"\n        Remove keycomments for keys that are not contained in the MetaDict.\n\n        Parameters\n        ----------\n        copy : `bool`, optional\n            Make a copy of the current keycomments dict before removing keys.\n        \"\"\"\n        if 'keycomments' not in self:\n            return\n\n        keycomments = self['keycomments']\n\n        if not isinstance(keycomments, dict):\n            raise TypeError(\n                \"'keycomments' key must have a value of type `dict`. Found \"\n                \"the following type: %r\" % type(keycomments))\n\n        if copy:\n            keycomments = keycomments.copy()\n\n        for key in list(keycomments.keys()):\n            if key not in self:\n                del keycomments[key]\n\n        self['keycomments'] = keycomments\n\n    def __contains__(self, key):\n        \"\"\"\n        Override ``__contains__``.\n        \"\"\"\n        return OrderedDict.__contains__(self, key.lower())\n\n    def __getitem__(self, key):\n        \"\"\"\n        Override ``[]`` indexing.\n        \"\"\"\n        return OrderedDict.__getitem__(self, key.lower())\n\n    def __setitem__(self, key, value):\n        \"\"\"\n        Override ``[]`` indexing.\n        \"\"\"\n        return OrderedDict.__setitem__(self, key.lower(), value)\n\n    # Note: `OrderedDict.popitem()` does not need to be overridden to prune\n    # keycomments because it calls `__delitem__` internally.\n    def __delitem__(self, key):\n        \"\"\"\n        Override ``del dict[key]`` key deletion.\n        \"\"\"\n        OrderedDict.__delitem__(self, key.lower())\n        self._prune_keycomments()\n\n    def get(self, key, default=None):\n        \"\"\"\n        Override ``.get()`` indexing.\n        \"\"\"\n        return OrderedDict.get(self, key.lower(), default)\n\n    def has_key(self, key):\n        \"\"\"\n        Override ``.has_key()`` to perform case-insensitively.\n        \"\"\"\n        return key.lower() in self\n\n    def pop(self, key, default=None):\n        \"\"\"\n        Override ``.pop()`` to perform case-insensitively.\n        \"\"\"\n        has_key = key in self\n        result = OrderedDict.pop(self, key.lower(), default)\n        if has_key:\n            self._prune_keycomments()\n        return result\n\n    def update(self, d2):\n        \"\"\"\n        Override ``.update()`` to perform case-insensitively.\n        \"\"\"\n        return OrderedDict.update(self, OrderedDict((k.lower(), v) for k, v in d2.items()))\n\n    def setdefault(self, key, default=None):\n        \"\"\"\n        Override ``.setdefault()`` to perform case-insensitively.\n        \"\"\"\n        return OrderedDict.setdefault(self, key.lower(), default)\n", "path": "sunpy/util/metadata.py"}]}