We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
CNV writing phase_mapping fails

This line:
https://github.com/obspy/obspy/blob/master/obspy/io/cnv/core.py#L63

doesn't work because 

``values.update(("P", "S"))`` returns ``None``

This works:
```python
values = set(phase_mapping.values())
values.update(("P","S"))
    if values != set(("P", "S")):

```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `obspy/io/cnv/core.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """
3 CNV file format support for ObsPy
4 
5 :copyright:
6     The ObsPy Development Team (devs@obspy.org)
7 :license:
8     GNU Lesser General Public License, Version 3
9     (https://www.gnu.org/copyleft/lesser.html)
10 """
11 from __future__ import (absolute_import, division, print_function,
12                         unicode_literals)
13 from future.builtins import *  # NOQA @UnusedWildImport
14 
15 from bisect import bisect_right
16 import warnings
17 
18 
19 def _write_cnv(catalog, filename, phase_mapping=None, ifx_list=None,
20                weight_mapping=None, default_weight=0):
21     """
22     Write a :class:`~obspy.core.event.Catalog` object to CNV event summary
23     format (used as event/pick input by VELEST program).
24 
25     .. warning::
26         This function should NOT be called directly, it registers via the
27         the :meth:`~obspy.core.event.Catalog.write` method of an
28         ObsPy :class:`~obspy.core.event.Catalog` object, call this instead.
29 
30     :type catalog: :class:`~obspy.core.event.Catalog`
31     :param catalog: Input catalog for CNV output..
32     :type filename: str or file
33     :param filename: Filename to write or open file-like object.
34     :type phase_mapping: dict
35     :param phase_mapping: Mapping of phase hints to "P" or "S". CNV format only
36         uses a single letter phase code (either "P" or "S"). If not specified
37         the following default mapping is used: 'p', 'P', 'Pg', 'Pn', 'Pm' will
38         be mapped to "P" and 's', 'S', 'Sg', 'Sn', 'Sm' will be mapped to "S".
39     :type ifx_list: list of :class:`~obspy.core.event.ResourceIdentifier`
40     :param ifx_list: List of events for which the 'IFX' flag should be set
41         (used in VELEST to fix the y coordinate of the hypocenter).
42     :type weight_mapping: list of float
43     :param weight_mapping: Mapping of pick uncertainties to integer weights.
44         (Sorted) list of floats of boundary uncertainties. If uncertainty of
45         pick is lower than the first entry of the list than a weight of 0 is
46         assigned. If it is larger than the first entry, but smaller than the
47         second entry a weight of 1 is assigned, and so on. The list of
48         uncertainty boundaries should not contain more than 9 entries because
49         the integer weight is restricted to a single digit. If not specified
50         all picks will be output with weight `default_weight`.
51     :type default_weight: int
52     :param default_weight: Default weight to use when pick has no timing
53         uncertainty and thus can not be mapped using `weight_mapping`
54         parameter. Default weight should not be larger than 9, as the weight is
55         represented as a single digit.
56     """
57     # Check phase mapping or use default one
58     if phase_mapping is None:
59         phase_mapping = {'p': "P", 'P': "P", 'Pg': "P", 'Pn': "P", 'Pm': "P",
60                          's': "S", 'S': "S", 'Sg': "S", 'Sn': "S", 'Sm': "S"}
61     else:
62         values = set(phase_mapping.values())
63         if values.update(("P", "S")) != set(("P", "S")):
64             msg = ("Values of phase mapping should only be 'P' or 'S'")
65             raise ValueError(msg)
66     if ifx_list is None:
67         ifx_list = []
68     if weight_mapping is None:
69         weight_mapping = []
70     else:
71         if list(weight_mapping) != sorted(weight_mapping):
72             msg = ("List of floats in weight mapping must be sorted in "
73                    "ascending order.")
74             raise ValueError(msg)
75 
76     out = []
77     for event in catalog:
78         o = event.preferred_origin() or event.origins[0]
79         m = event.preferred_magnitude() or event.magnitudes[0]
80 
81         out_ = "%s %5.2f %7.4f%1s %8.4f%1s%7.2f%7.2f%2i\n"
82         cns = o.latitude >= 0 and "N" or "S"
83         cew = o.longitude >= 0 and "E" or "W"
84         if event.resource_id in ifx_list:
85             ifx = 1
86         else:
87             ifx = 0
88         out_ = out_ % (o.time.strftime("%y%m%d %H%M"),
89                        o.time.second + o.time.microsecond / 1e6,
90                        abs(o.latitude), cns, abs(o.longitude), cew,
91                        o.depth / 1e3, m.mag, ifx)
92         # assemble phase info
93         picks = []
94         for p in event.picks:
95             # map uncertainty to integer weight
96             if p.time_errors.upper_uncertainty is not None and \
97                     p.time_errors.lower_uncertainty is not None:
98                 uncertainty = p.time_errors.upper_uncertainty + \
99                     p.time_errors.lower_uncertainty
100             else:
101                 uncertainty = p.time_errors.uncertainty
102             if uncertainty is None:
103                 msg = ("No pick time uncertainty, pick will be mapped to "
104                        "default integer weight (%s).") % default_weight
105                 warnings.warn(msg)
106                 weight = default_weight
107             else:
108                 weight = bisect_right(weight_mapping, uncertainty)
109             if weight > 9:
110                 msg = ("Integer weight for pick is greater than 9. "
111                        "This is not compatible with the single-character "
112                        "field for pick weight in CNV format."
113                        "Using 9 as integer weight.")
114                 warnings.warn(msg)
115                 weight = 9
116             # map phase hint
117             phase = phase_mapping.get(p.phase_hint, None)
118             if phase is None:
119                 msg = "Skipping pick (%s) with unmapped phase hint: %s"
120                 msg = msg % (p.waveform_id.get_seed_string(), p.phase_hint)
121                 warnings.warn(msg)
122                 continue
123             station = p.waveform_id.station_code
124             if len(station) > 4:
125                 msg = ("Station code with more than 4 characters detected. "
126                        "Only the first 4 characters will be used in output.")
127                 warnings.warn(msg)
128                 station = station[:4]
129             dt = "%6.2f" % (p.time - o.time)
130             if len(dt) != 6:
131                 msg = ("Problem with pick (%s): Calculated travel time '%s' "
132                        "does not fit in the '%%6.2f' fixed format field. "
133                        "Skipping this pick.")
134                 msg = msg % (p.waveform_id.get_seed_string(), dt)
135                 warnings.warn(msg)
136                 continue
137             picks.append("".join([station.ljust(4), phase, str(weight), dt]))
138         while len(picks) > 6:
139             next_picks, picks = picks[:6], picks[6:]
140             out_ += "".join(next_picks) + "\n"
141         if picks:
142             out_ += "".join(picks) + "\n"
143         out.append(out_)
144 
145     if out:
146         out = "\n".join(out + [""])
147     else:
148         msg = "No event/pick information, writing empty CNV file."
149         warnings.warn(msg)
150 
151     # Open filehandler or use an existing file like object.
152     if not hasattr(filename, "write"):
153         file_opened = True
154         fh = open(filename, "wb")
155     else:
156         file_opened = False
157         fh = filename
158 
159     fh.write(out.encode())
160 
161     # Close if a file has been opened by this function.
162     if file_opened is True:
163         fh.close()
164 
165 
166 if __name__ == '__main__':
167     import doctest
168     doctest.testmod(exclude_empty=True)
169
```
--- 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/obspy/io/cnv/core.py b/obspy/io/cnv/core.py
--- a/obspy/io/cnv/core.py
+++ b/obspy/io/cnv/core.py
@@ -60,7 +60,8 @@
                          's': "S", 'S': "S", 'Sg': "S", 'Sn': "S", 'Sm': "S"}
     else:
         values = set(phase_mapping.values())
-        if values.update(("P", "S")) != set(("P", "S")):
+        values.update(("P", "S"))
+        if values != set(("P", "S")):
             msg = ("Values of phase mapping should only be 'P' or 'S'")
             raise ValueError(msg)
     if ifx_list is None:

{"golden_diff": "diff --git a/obspy/io/cnv/core.py b/obspy/io/cnv/core.py\n--- a/obspy/io/cnv/core.py\n+++ b/obspy/io/cnv/core.py\n@@ -60,7 +60,8 @@\n                          's': \"S\", 'S': \"S\", 'Sg': \"S\", 'Sn': \"S\", 'Sm': \"S\"}\n     else:\n         values = set(phase_mapping.values())\n-        if values.update((\"P\", \"S\")) != set((\"P\", \"S\")):\n+        values.update((\"P\", \"S\"))\n+        if values != set((\"P\", \"S\")):\n             msg = (\"Values of phase mapping should only be 'P' or 'S'\")\n             raise ValueError(msg)\n     if ifx_list is None:\n", "issue": "CNV writing phase_mapping fails\n\r\nThis line:\r\nhttps://github.com/obspy/obspy/blob/master/obspy/io/cnv/core.py#L63\r\n\r\ndoesn't work because \r\n\r\n``values.update((\"P\", \"S\"))`` returns ``None``\r\n\r\nThis works:\r\n```python\r\nvalues = set(phase_mapping.values())\r\nvalues.update((\"P\",\"S\"))\r\n    if values != set((\"P\", \"S\")):\r\n\r\n```\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nCNV file format support for ObsPy\n\n:copyright:\n    The ObsPy Development Team (devs@obspy.org)\n:license:\n    GNU Lesser General Public License, Version 3\n    (https://www.gnu.org/copyleft/lesser.html)\n\"\"\"\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\nfrom future.builtins import *  # NOQA @UnusedWildImport\n\nfrom bisect import bisect_right\nimport warnings\n\n\ndef _write_cnv(catalog, filename, phase_mapping=None, ifx_list=None,\n               weight_mapping=None, default_weight=0):\n    \"\"\"\n    Write a :class:`~obspy.core.event.Catalog` object to CNV event summary\n    format (used as event/pick input by VELEST program).\n\n    .. warning::\n        This function should NOT be called directly, it registers via the\n        the :meth:`~obspy.core.event.Catalog.write` method of an\n        ObsPy :class:`~obspy.core.event.Catalog` object, call this instead.\n\n    :type catalog: :class:`~obspy.core.event.Catalog`\n    :param catalog: Input catalog for CNV output..\n    :type filename: str or file\n    :param filename: Filename to write or open file-like object.\n    :type phase_mapping: dict\n    :param phase_mapping: Mapping of phase hints to \"P\" or \"S\". CNV format only\n        uses a single letter phase code (either \"P\" or \"S\"). If not specified\n        the following default mapping is used: 'p', 'P', 'Pg', 'Pn', 'Pm' will\n        be mapped to \"P\" and 's', 'S', 'Sg', 'Sn', 'Sm' will be mapped to \"S\".\n    :type ifx_list: list of :class:`~obspy.core.event.ResourceIdentifier`\n    :param ifx_list: List of events for which the 'IFX' flag should be set\n        (used in VELEST to fix the y coordinate of the hypocenter).\n    :type weight_mapping: list of float\n    :param weight_mapping: Mapping of pick uncertainties to integer weights.\n        (Sorted) list of floats of boundary uncertainties. If uncertainty of\n        pick is lower than the first entry of the list than a weight of 0 is\n        assigned. If it is larger than the first entry, but smaller than the\n        second entry a weight of 1 is assigned, and so on. The list of\n        uncertainty boundaries should not contain more than 9 entries because\n        the integer weight is restricted to a single digit. If not specified\n        all picks will be output with weight `default_weight`.\n    :type default_weight: int\n    :param default_weight: Default weight to use when pick has no timing\n        uncertainty and thus can not be mapped using `weight_mapping`\n        parameter. Default weight should not be larger than 9, as the weight is\n        represented as a single digit.\n    \"\"\"\n    # Check phase mapping or use default one\n    if phase_mapping is None:\n        phase_mapping = {'p': \"P\", 'P': \"P\", 'Pg': \"P\", 'Pn': \"P\", 'Pm': \"P\",\n                         's': \"S\", 'S': \"S\", 'Sg': \"S\", 'Sn': \"S\", 'Sm': \"S\"}\n    else:\n        values = set(phase_mapping.values())\n        if values.update((\"P\", \"S\")) != set((\"P\", \"S\")):\n            msg = (\"Values of phase mapping should only be 'P' or 'S'\")\n            raise ValueError(msg)\n    if ifx_list is None:\n        ifx_list = []\n    if weight_mapping is None:\n        weight_mapping = []\n    else:\n        if list(weight_mapping) != sorted(weight_mapping):\n            msg = (\"List of floats in weight mapping must be sorted in \"\n                   \"ascending order.\")\n            raise ValueError(msg)\n\n    out = []\n    for event in catalog:\n        o = event.preferred_origin() or event.origins[0]\n        m = event.preferred_magnitude() or event.magnitudes[0]\n\n        out_ = \"%s %5.2f %7.4f%1s %8.4f%1s%7.2f%7.2f%2i\\n\"\n        cns = o.latitude >= 0 and \"N\" or \"S\"\n        cew = o.longitude >= 0 and \"E\" or \"W\"\n        if event.resource_id in ifx_list:\n            ifx = 1\n        else:\n            ifx = 0\n        out_ = out_ % (o.time.strftime(\"%y%m%d %H%M\"),\n                       o.time.second + o.time.microsecond / 1e6,\n                       abs(o.latitude), cns, abs(o.longitude), cew,\n                       o.depth / 1e3, m.mag, ifx)\n        # assemble phase info\n        picks = []\n        for p in event.picks:\n            # map uncertainty to integer weight\n            if p.time_errors.upper_uncertainty is not None and \\\n                    p.time_errors.lower_uncertainty is not None:\n                uncertainty = p.time_errors.upper_uncertainty + \\\n                    p.time_errors.lower_uncertainty\n            else:\n                uncertainty = p.time_errors.uncertainty\n            if uncertainty is None:\n                msg = (\"No pick time uncertainty, pick will be mapped to \"\n                       \"default integer weight (%s).\") % default_weight\n                warnings.warn(msg)\n                weight = default_weight\n            else:\n                weight = bisect_right(weight_mapping, uncertainty)\n            if weight > 9:\n                msg = (\"Integer weight for pick is greater than 9. \"\n                       \"This is not compatible with the single-character \"\n                       \"field for pick weight in CNV format.\"\n                       \"Using 9 as integer weight.\")\n                warnings.warn(msg)\n                weight = 9\n            # map phase hint\n            phase = phase_mapping.get(p.phase_hint, None)\n            if phase is None:\n                msg = \"Skipping pick (%s) with unmapped phase hint: %s\"\n                msg = msg % (p.waveform_id.get_seed_string(), p.phase_hint)\n                warnings.warn(msg)\n                continue\n            station = p.waveform_id.station_code\n            if len(station) > 4:\n                msg = (\"Station code with more than 4 characters detected. \"\n                       \"Only the first 4 characters will be used in output.\")\n                warnings.warn(msg)\n                station = station[:4]\n            dt = \"%6.2f\" % (p.time - o.time)\n            if len(dt) != 6:\n                msg = (\"Problem with pick (%s): Calculated travel time '%s' \"\n                       \"does not fit in the '%%6.2f' fixed format field. \"\n                       \"Skipping this pick.\")\n                msg = msg % (p.waveform_id.get_seed_string(), dt)\n                warnings.warn(msg)\n                continue\n            picks.append(\"\".join([station.ljust(4), phase, str(weight), dt]))\n        while len(picks) > 6:\n            next_picks, picks = picks[:6], picks[6:]\n            out_ += \"\".join(next_picks) + \"\\n\"\n        if picks:\n            out_ += \"\".join(picks) + \"\\n\"\n        out.append(out_)\n\n    if out:\n        out = \"\\n\".join(out + [\"\"])\n    else:\n        msg = \"No event/pick information, writing empty CNV file.\"\n        warnings.warn(msg)\n\n    # Open filehandler or use an existing file like object.\n    if not hasattr(filename, \"write\"):\n        file_opened = True\n        fh = open(filename, \"wb\")\n    else:\n        file_opened = False\n        fh = filename\n\n    fh.write(out.encode())\n\n    # Close if a file has been opened by this function.\n    if file_opened is True:\n        fh.close()\n\n\nif __name__ == '__main__':\n    import doctest\n    doctest.testmod(exclude_empty=True)\n", "path": "obspy/io/cnv/core.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nCNV file format support for ObsPy\n\n:copyright:\n    The ObsPy Development Team (devs@obspy.org)\n:license:\n    GNU Lesser General Public License, Version 3\n    (https://www.gnu.org/copyleft/lesser.html)\n\"\"\"\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\nfrom future.builtins import *  # NOQA @UnusedWildImport\n\nfrom bisect import bisect_right\nimport warnings\n\n\ndef _write_cnv(catalog, filename, phase_mapping=None, ifx_list=None,\n               weight_mapping=None, default_weight=0):\n    \"\"\"\n    Write a :class:`~obspy.core.event.Catalog` object to CNV event summary\n    format (used as event/pick input by VELEST program).\n\n    .. warning::\n        This function should NOT be called directly, it registers via the\n        the :meth:`~obspy.core.event.Catalog.write` method of an\n        ObsPy :class:`~obspy.core.event.Catalog` object, call this instead.\n\n    :type catalog: :class:`~obspy.core.event.Catalog`\n    :param catalog: Input catalog for CNV output..\n    :type filename: str or file\n    :param filename: Filename to write or open file-like object.\n    :type phase_mapping: dict\n    :param phase_mapping: Mapping of phase hints to \"P\" or \"S\". CNV format only\n        uses a single letter phase code (either \"P\" or \"S\"). If not specified\n        the following default mapping is used: 'p', 'P', 'Pg', 'Pn', 'Pm' will\n        be mapped to \"P\" and 's', 'S', 'Sg', 'Sn', 'Sm' will be mapped to \"S\".\n    :type ifx_list: list of :class:`~obspy.core.event.ResourceIdentifier`\n    :param ifx_list: List of events for which the 'IFX' flag should be set\n        (used in VELEST to fix the y coordinate of the hypocenter).\n    :type weight_mapping: list of float\n    :param weight_mapping: Mapping of pick uncertainties to integer weights.\n        (Sorted) list of floats of boundary uncertainties. If uncertainty of\n        pick is lower than the first entry of the list than a weight of 0 is\n        assigned. If it is larger than the first entry, but smaller than the\n        second entry a weight of 1 is assigned, and so on. The list of\n        uncertainty boundaries should not contain more than 9 entries because\n        the integer weight is restricted to a single digit. If not specified\n        all picks will be output with weight `default_weight`.\n    :type default_weight: int\n    :param default_weight: Default weight to use when pick has no timing\n        uncertainty and thus can not be mapped using `weight_mapping`\n        parameter. Default weight should not be larger than 9, as the weight is\n        represented as a single digit.\n    \"\"\"\n    # Check phase mapping or use default one\n    if phase_mapping is None:\n        phase_mapping = {'p': \"P\", 'P': \"P\", 'Pg': \"P\", 'Pn': \"P\", 'Pm': \"P\",\n                         's': \"S\", 'S': \"S\", 'Sg': \"S\", 'Sn': \"S\", 'Sm': \"S\"}\n    else:\n        values = set(phase_mapping.values())\n        values.update((\"P\", \"S\"))\n        if values != set((\"P\", \"S\")):\n            msg = (\"Values of phase mapping should only be 'P' or 'S'\")\n            raise ValueError(msg)\n    if ifx_list is None:\n        ifx_list = []\n    if weight_mapping is None:\n        weight_mapping = []\n    else:\n        if list(weight_mapping) != sorted(weight_mapping):\n            msg = (\"List of floats in weight mapping must be sorted in \"\n                   \"ascending order.\")\n            raise ValueError(msg)\n\n    out = []\n    for event in catalog:\n        o = event.preferred_origin() or event.origins[0]\n        m = event.preferred_magnitude() or event.magnitudes[0]\n\n        out_ = \"%s %5.2f %7.4f%1s %8.4f%1s%7.2f%7.2f%2i\\n\"\n        cns = o.latitude >= 0 and \"N\" or \"S\"\n        cew = o.longitude >= 0 and \"E\" or \"W\"\n        if event.resource_id in ifx_list:\n            ifx = 1\n        else:\n            ifx = 0\n        out_ = out_ % (o.time.strftime(\"%y%m%d %H%M\"),\n                       o.time.second + o.time.microsecond / 1e6,\n                       abs(o.latitude), cns, abs(o.longitude), cew,\n                       o.depth / 1e3, m.mag, ifx)\n        # assemble phase info\n        picks = []\n        for p in event.picks:\n            # map uncertainty to integer weight\n            if p.time_errors.upper_uncertainty is not None and \\\n                    p.time_errors.lower_uncertainty is not None:\n                uncertainty = p.time_errors.upper_uncertainty + \\\n                    p.time_errors.lower_uncertainty\n            else:\n                uncertainty = p.time_errors.uncertainty\n            if uncertainty is None:\n                msg = (\"No pick time uncertainty, pick will be mapped to \"\n                       \"default integer weight (%s).\") % default_weight\n                warnings.warn(msg)\n                weight = default_weight\n            else:\n                weight = bisect_right(weight_mapping, uncertainty)\n            if weight > 9:\n                msg = (\"Integer weight for pick is greater than 9. \"\n                       \"This is not compatible with the single-character \"\n                       \"field for pick weight in CNV format.\"\n                       \"Using 9 as integer weight.\")\n                warnings.warn(msg)\n                weight = 9\n            # map phase hint\n            phase = phase_mapping.get(p.phase_hint, None)\n            if phase is None:\n                msg = \"Skipping pick (%s) with unmapped phase hint: %s\"\n                msg = msg % (p.waveform_id.get_seed_string(), p.phase_hint)\n                warnings.warn(msg)\n                continue\n            station = p.waveform_id.station_code\n            if len(station) > 4:\n                msg = (\"Station code with more than 4 characters detected. \"\n                       \"Only the first 4 characters will be used in output.\")\n                warnings.warn(msg)\n                station = station[:4]\n            dt = \"%6.2f\" % (p.time - o.time)\n            if len(dt) != 6:\n                msg = (\"Problem with pick (%s): Calculated travel time '%s' \"\n                       \"does not fit in the '%%6.2f' fixed format field. \"\n                       \"Skipping this pick.\")\n                msg = msg % (p.waveform_id.get_seed_string(), dt)\n                warnings.warn(msg)\n                continue\n            picks.append(\"\".join([station.ljust(4), phase, str(weight), dt]))\n        while len(picks) > 6:\n            next_picks, picks = picks[:6], picks[6:]\n            out_ += \"\".join(next_picks) + \"\\n\"\n        if picks:\n            out_ += \"\".join(picks) + \"\\n\"\n        out.append(out_)\n\n    if out:\n        out = \"\\n\".join(out + [\"\"])\n    else:\n        msg = \"No event/pick information, writing empty CNV file.\"\n        warnings.warn(msg)\n\n    # Open filehandler or use an existing file like object.\n    if not hasattr(filename, \"write\"):\n        file_opened = True\n        fh = open(filename, \"wb\")\n    else:\n        file_opened = False\n        fh = filename\n\n    fh.write(out.encode())\n\n    # Close if a file has been opened by this function.\n    if file_opened is True:\n        fh.close()\n\n\nif __name__ == '__main__':\n    import doctest\n    doctest.testmod(exclude_empty=True)\n", "path": "obspy/io/cnv/core.py"}]}