prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Dealing with empty values in `DelimitedFieldMixin`
  `DelimitedList(String())` deserializes "a,,c" as `["a", "", "c"]`.
  
  I guess this meets user expectations.
  
  My expectation with integers would be that
  
  `DelimitedList(Integer(allow_none=True))` deserializes `"1,,3"` as `[1,None,3]`
  
  but it errors.
  
  The reason for this is that when the string is split, it is turned into `["1", "", "3"]`. This is why it works in the string case.
  
  I'm not sure this was really intended. It may be a side effect of the `split` function that happens to do well with strings.
  
  We could change that to replace empty values with `None`. But it would break the string use case, unless the user overloads `String` field to deserialize `None` as `""`.
  
  Likewise, users may already overload `Integer` to deserialize `""` as `None` and no modification is required to `DelimitedFieldMixin`.
  
  Just been caught by this and wondering out loud. Advice welcome.
  
  In any case, there is an intrinsic limitation in the delimited string format: one can't distinguish empty string from missing value (as opposed to a JSON payload). It is not clear to me how OpenAPI (for instance) deals with the case of an empty element in an array (in a query argument).
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/webargs/fields.py`
  Content:
  ```
  1 """Field classes.
  2 
  3 Includes all fields from `marshmallow.fields` in addition to a custom
  4 `Nested` field and `DelimitedList`.
  5 
  6 All fields can optionally take a special `location` keyword argument, which
  7 tells webargs where to parse the request argument from.
  8 
  9 .. code-block:: python
  10 
  11     args = {
  12         "active": fields.Bool(location="query"),
  13         "content_type": fields.Str(data_key="Content-Type", location="headers"),
  14     }
  15 """
  16 from __future__ import annotations
  17 
  18 import marshmallow as ma
  19 
  20 # Expose all fields from marshmallow.fields.
  21 from marshmallow.fields import *  # noqa: F40
  22 
  23 __all__ = ["DelimitedList", "DelimitedTuple"] + ma.fields.__all__
  24 
  25 
  26 # TODO: remove custom `Nested` in the next major release
  27 #
  28 # the `Nested` class is only needed on versions of marshmallow prior to v3.15.0
  29 # in that version, `ma.fields.Nested` gained the ability to consume dict inputs
  30 # prior to that, this subclass adds this capability
  31 #
  32 # if we drop support for ma.__version_info__ < (3, 15) we can do this
  33 class Nested(ma.fields.Nested):  # type: ignore[no-redef]
  34     """Same as `marshmallow.fields.Nested`, except can be passed a dictionary as
  35     the first argument, which will be converted to a `marshmallow.Schema`.
  36 
  37     .. note::
  38 
  39         The schema class here will always be `marshmallow.Schema`, regardless
  40         of whether a custom schema class is set on the parser. Pass an explicit schema
  41         class if necessary.
  42     """
  43 
  44     def __init__(self, nested, *args, **kwargs):
  45         if isinstance(nested, dict):
  46             nested = ma.Schema.from_dict(nested)
  47         super().__init__(nested, *args, **kwargs)
  48 
  49 
  50 class DelimitedFieldMixin:
  51     """
  52     This is a mixin class for subclasses of ma.fields.List and ma.fields.Tuple
  53     which split on a pre-specified delimiter. By default, the delimiter will be ","
  54 
  55     Because we want the MRO to reach this class before the List or Tuple class,
  56     it must be listed first in the superclasses
  57 
  58     For example, a DelimitedList-like type can be defined like so:
  59 
  60     >>> class MyDelimitedList(DelimitedFieldMixin, ma.fields.List):
  61     >>>     pass
  62     """
  63 
  64     delimiter: str = ","
  65     # delimited fields set is_multiple=False for webargs.core.is_multiple
  66     is_multiple: bool = False
  67 
  68     def _serialize(self, value, attr, obj, **kwargs):
  69         # serializing will start with parent-class serialization, so that we correctly
  70         # output lists of non-primitive types, e.g. DelimitedList(DateTime)
  71         return self.delimiter.join(
  72             format(each) for each in super()._serialize(value, attr, obj, **kwargs)
  73         )
  74 
  75     def _deserialize(self, value, attr, data, **kwargs):
  76         # attempting to deserialize from a non-string source is an error
  77         if not isinstance(value, (str, bytes)):
  78             raise self.make_error("invalid")
  79         values = value.split(self.delimiter) if value else []
  80         return super()._deserialize(values, attr, data, **kwargs)
  81 
  82 
  83 class DelimitedList(DelimitedFieldMixin, ma.fields.List):
  84     """A field which is similar to a List, but takes its input as a delimited
  85     string (e.g. "foo,bar,baz").
  86 
  87     Like List, it can be given a nested field type which it will use to
  88     de/serialize each element of the list.
  89 
  90     :param Field cls_or_instance: A field class or instance.
  91     :param str delimiter: Delimiter between values.
  92     """
  93 
  94     default_error_messages = {"invalid": "Not a valid delimited list."}
  95 
  96     def __init__(
  97         self,
  98         cls_or_instance: ma.fields.Field | type,
  99         *,
  100         delimiter: str | None = None,
  101         **kwargs,
  102     ):
  103         self.delimiter = delimiter or self.delimiter
  104         super().__init__(cls_or_instance, **kwargs)
  105 
  106 
  107 class DelimitedTuple(DelimitedFieldMixin, ma.fields.Tuple):
  108     """A field which is similar to a Tuple, but takes its input as a delimited
  109     string (e.g. "foo,bar,baz").
  110 
  111     Like Tuple, it can be given a tuple of nested field types which it will use to
  112     de/serialize each element of the tuple.
  113 
  114     :param Iterable[Field] tuple_fields: An iterable of field classes or instances.
  115     :param str delimiter: Delimiter between values.
  116     """
  117 
  118     default_error_messages = {"invalid": "Not a valid delimited tuple."}
  119 
  120     def __init__(self, tuple_fields, *, delimiter: str | None = None, **kwargs):
  121         self.delimiter = delimiter or self.delimiter
  122         super().__init__(tuple_fields, **kwargs)
  123
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/webargs/fields.py b/src/webargs/fields.py
  --- a/src/webargs/fields.py
  +++ b/src/webargs/fields.py
  @@ -15,6 +15,8 @@
   """
   from __future__ import annotations
   
  +import typing
  +
   import marshmallow as ma
   
   # Expose all fields from marshmallow.fields.
  @@ -64,6 +66,8 @@
       delimiter: str = ","
       # delimited fields set is_multiple=False for webargs.core.is_multiple
       is_multiple: bool = False
  +    # NOTE: in 8.x this defaults to "" but in 9.x it will be 'missing'
  +    empty_value: typing.Any = ""
   
       def _serialize(self, value, attr, obj, **kwargs):
           # serializing will start with parent-class serialization, so that we correctly
  @@ -77,6 +81,8 @@
           if not isinstance(value, (str, bytes)):
               raise self.make_error("invalid")
           values = value.split(self.delimiter) if value else []
  +        # convert empty strings to the empty value; typically "" and therefore a no-op
  +        values = [v or self.empty_value for v in values]
           return super()._deserialize(values, attr, data, **kwargs)
   
   
  @@ -117,6 +123,12 @@
   
       default_error_messages = {"invalid": "Not a valid delimited tuple."}
   
  -    def __init__(self, tuple_fields, *, delimiter: str | None = None, **kwargs):
  +    def __init__(
  +        self,
  +        tuple_fields,
  +        *,
  +        delimiter: str | None = None,
  +        **kwargs,
  +    ):
           self.delimiter = delimiter or self.delimiter
           super().__init__(tuple_fields, **kwargs)
  
verification_info: {"golden_diff": "diff --git a/src/webargs/fields.py b/src/webargs/fields.py\n--- a/src/webargs/fields.py\n+++ b/src/webargs/fields.py\n@@ -15,6 +15,8 @@\n \"\"\"\n from __future__ import annotations\n \n+import typing\n+\n import marshmallow as ma\n \n # Expose all fields from marshmallow.fields.\n@@ -64,6 +66,8 @@\n     delimiter: str = \",\"\n     # delimited fields set is_multiple=False for webargs.core.is_multiple\n     is_multiple: bool = False\n+    # NOTE: in 8.x this defaults to \"\" but in 9.x it will be 'missing'\n+    empty_value: typing.Any = \"\"\n \n     def _serialize(self, value, attr, obj, **kwargs):\n         # serializing will start with parent-class serialization, so that we correctly\n@@ -77,6 +81,8 @@\n         if not isinstance(value, (str, bytes)):\n             raise self.make_error(\"invalid\")\n         values = value.split(self.delimiter) if value else []\n+        # convert empty strings to the empty value; typically \"\" and therefore a no-op\n+        values = [v or self.empty_value for v in values]\n         return super()._deserialize(values, attr, data, **kwargs)\n \n \n@@ -117,6 +123,12 @@\n \n     default_error_messages = {\"invalid\": \"Not a valid delimited tuple.\"}\n \n-    def __init__(self, tuple_fields, *, delimiter: str | None = None, **kwargs):\n+    def __init__(\n+        self,\n+        tuple_fields,\n+        *,\n+        delimiter: str | None = None,\n+        **kwargs,\n+    ):\n         self.delimiter = delimiter or self.delimiter\n         super().__init__(tuple_fields, **kwargs)\n", "issue": "Dealing with empty values in `DelimitedFieldMixin`\n`DelimitedList(String())` deserializes \"a,,c\" as `[\"a\", \"\", \"c\"]`.\r\n\r\nI guess this meets user expectations.\r\n\r\nMy expectation with integers would be that\r\n\r\n`DelimitedList(Integer(allow_none=True))` deserializes `\"1,,3\"` as `[1,None,3]`\r\n\r\nbut it errors.\r\n\r\nThe reason for this is that when the string is split, it is turned into `[\"1\", \"\", \"3\"]`. This is why it works in the string case.\r\n\r\nI'm not sure this was really intended. It may be a side effect of the `split` function that happens to do well with strings.\r\n\r\nWe could change that to replace empty values with `None`. But it would break the string use case, unless the user overloads `String` field to deserialize `None` as `\"\"`.\r\n\r\nLikewise, users may already overload `Integer` to deserialize `\"\"` as `None` and no modification is required to `DelimitedFieldMixin`.\r\n\r\nJust been caught by this and wondering out loud. Advice welcome.\r\n\r\nIn any case, there is an intrinsic limitation in the delimited string format: one can't distinguish empty string from missing value (as opposed to a JSON payload). It is not clear to me how OpenAPI (for instance) deals with the case of an empty element in an array (in a query argument).\n", "before_files": [{"content": "\"\"\"Field classes.\n\nIncludes all fields from `marshmallow.fields` in addition to a custom\n`Nested` field and `DelimitedList`.\n\nAll fields can optionally take a special `location` keyword argument, which\ntells webargs where to parse the request argument from.\n\n.. code-block:: python\n\n    args = {\n        \"active\": fields.Bool(location=\"query\"),\n        \"content_type\": fields.Str(data_key=\"Content-Type\", location=\"headers\"),\n    }\n\"\"\"\nfrom __future__ import annotations\n\nimport marshmallow as ma\n\n# Expose all fields from marshmallow.fields.\nfrom marshmallow.fields import *  # noqa: F40\n\n__all__ = [\"DelimitedList\", \"DelimitedTuple\"] + ma.fields.__all__\n\n\n# TODO: remove custom `Nested` in the next major release\n#\n# the `Nested` class is only needed on versions of marshmallow prior to v3.15.0\n# in that version, `ma.fields.Nested` gained the ability to consume dict inputs\n# prior to that, this subclass adds this capability\n#\n# if we drop support for ma.__version_info__ < (3, 15) we can do this\nclass Nested(ma.fields.Nested):  # type: ignore[no-redef]\n    \"\"\"Same as `marshmallow.fields.Nested`, except can be passed a dictionary as\n    the first argument, which will be converted to a `marshmallow.Schema`.\n\n    .. note::\n\n        The schema class here will always be `marshmallow.Schema`, regardless\n        of whether a custom schema class is set on the parser. Pass an explicit schema\n        class if necessary.\n    \"\"\"\n\n    def __init__(self, nested, *args, **kwargs):\n        if isinstance(nested, dict):\n            nested = ma.Schema.from_dict(nested)\n        super().__init__(nested, *args, **kwargs)\n\n\nclass DelimitedFieldMixin:\n    \"\"\"\n    This is a mixin class for subclasses of ma.fields.List and ma.fields.Tuple\n    which split on a pre-specified delimiter. By default, the delimiter will be \",\"\n\n    Because we want the MRO to reach this class before the List or Tuple class,\n    it must be listed first in the superclasses\n\n    For example, a DelimitedList-like type can be defined like so:\n\n    >>> class MyDelimitedList(DelimitedFieldMixin, ma.fields.List):\n    >>>     pass\n    \"\"\"\n\n    delimiter: str = \",\"\n    # delimited fields set is_multiple=False for webargs.core.is_multiple\n    is_multiple: bool = False\n\n    def _serialize(self, value, attr, obj, **kwargs):\n        # serializing will start with parent-class serialization, so that we correctly\n        # output lists of non-primitive types, e.g. DelimitedList(DateTime)\n        return self.delimiter.join(\n            format(each) for each in super()._serialize(value, attr, obj, **kwargs)\n        )\n\n    def _deserialize(self, value, attr, data, **kwargs):\n        # attempting to deserialize from a non-string source is an error\n        if not isinstance(value, (str, bytes)):\n            raise self.make_error(\"invalid\")\n        values = value.split(self.delimiter) if value else []\n        return super()._deserialize(values, attr, data, **kwargs)\n\n\nclass DelimitedList(DelimitedFieldMixin, ma.fields.List):\n    \"\"\"A field which is similar to a List, but takes its input as a delimited\n    string (e.g. \"foo,bar,baz\").\n\n    Like List, it can be given a nested field type which it will use to\n    de/serialize each element of the list.\n\n    :param Field cls_or_instance: A field class or instance.\n    :param str delimiter: Delimiter between values.\n    \"\"\"\n\n    default_error_messages = {\"invalid\": \"Not a valid delimited list.\"}\n\n    def __init__(\n        self,\n        cls_or_instance: ma.fields.Field | type,\n        *,\n        delimiter: str | None = None,\n        **kwargs,\n    ):\n        self.delimiter = delimiter or self.delimiter\n        super().__init__(cls_or_instance, **kwargs)\n\n\nclass DelimitedTuple(DelimitedFieldMixin, ma.fields.Tuple):\n    \"\"\"A field which is similar to a Tuple, but takes its input as a delimited\n    string (e.g. \"foo,bar,baz\").\n\n    Like Tuple, it can be given a tuple of nested field types which it will use to\n    de/serialize each element of the tuple.\n\n    :param Iterable[Field] tuple_fields: An iterable of field classes or instances.\n    :param str delimiter: Delimiter between values.\n    \"\"\"\n\n    default_error_messages = {\"invalid\": \"Not a valid delimited tuple.\"}\n\n    def __init__(self, tuple_fields, *, delimiter: str | None = None, **kwargs):\n        self.delimiter = delimiter or self.delimiter\n        super().__init__(tuple_fields, **kwargs)\n", "path": "src/webargs/fields.py"}], "after_files": [{"content": "\"\"\"Field classes.\n\nIncludes all fields from `marshmallow.fields` in addition to a custom\n`Nested` field and `DelimitedList`.\n\nAll fields can optionally take a special `location` keyword argument, which\ntells webargs where to parse the request argument from.\n\n.. code-block:: python\n\n    args = {\n        \"active\": fields.Bool(location=\"query\"),\n        \"content_type\": fields.Str(data_key=\"Content-Type\", location=\"headers\"),\n    }\n\"\"\"\nfrom __future__ import annotations\n\nimport typing\n\nimport marshmallow as ma\n\n# Expose all fields from marshmallow.fields.\nfrom marshmallow.fields import *  # noqa: F40\n\n__all__ = [\"DelimitedList\", \"DelimitedTuple\"] + ma.fields.__all__\n\n\n# TODO: remove custom `Nested` in the next major release\n#\n# the `Nested` class is only needed on versions of marshmallow prior to v3.15.0\n# in that version, `ma.fields.Nested` gained the ability to consume dict inputs\n# prior to that, this subclass adds this capability\n#\n# if we drop support for ma.__version_info__ < (3, 15) we can do this\nclass Nested(ma.fields.Nested):  # type: ignore[no-redef]\n    \"\"\"Same as `marshmallow.fields.Nested`, except can be passed a dictionary as\n    the first argument, which will be converted to a `marshmallow.Schema`.\n\n    .. note::\n\n        The schema class here will always be `marshmallow.Schema`, regardless\n        of whether a custom schema class is set on the parser. Pass an explicit schema\n        class if necessary.\n    \"\"\"\n\n    def __init__(self, nested, *args, **kwargs):\n        if isinstance(nested, dict):\n            nested = ma.Schema.from_dict(nested)\n        super().__init__(nested, *args, **kwargs)\n\n\nclass DelimitedFieldMixin:\n    \"\"\"\n    This is a mixin class for subclasses of ma.fields.List and ma.fields.Tuple\n    which split on a pre-specified delimiter. By default, the delimiter will be \",\"\n\n    Because we want the MRO to reach this class before the List or Tuple class,\n    it must be listed first in the superclasses\n\n    For example, a DelimitedList-like type can be defined like so:\n\n    >>> class MyDelimitedList(DelimitedFieldMixin, ma.fields.List):\n    >>>     pass\n    \"\"\"\n\n    delimiter: str = \",\"\n    # delimited fields set is_multiple=False for webargs.core.is_multiple\n    is_multiple: bool = False\n    # NOTE: in 8.x this defaults to \"\" but in 9.x it will be 'missing'\n    empty_value: typing.Any = \"\"\n\n    def _serialize(self, value, attr, obj, **kwargs):\n        # serializing will start with parent-class serialization, so that we correctly\n        # output lists of non-primitive types, e.g. DelimitedList(DateTime)\n        return self.delimiter.join(\n            format(each) for each in super()._serialize(value, attr, obj, **kwargs)\n        )\n\n    def _deserialize(self, value, attr, data, **kwargs):\n        # attempting to deserialize from a non-string source is an error\n        if not isinstance(value, (str, bytes)):\n            raise self.make_error(\"invalid\")\n        values = value.split(self.delimiter) if value else []\n        # convert empty strings to the empty value; typically \"\" and therefore a no-op\n        values = [v or self.empty_value for v in values]\n        return super()._deserialize(values, attr, data, **kwargs)\n\n\nclass DelimitedList(DelimitedFieldMixin, ma.fields.List):\n    \"\"\"A field which is similar to a List, but takes its input as a delimited\n    string (e.g. \"foo,bar,baz\").\n\n    Like List, it can be given a nested field type which it will use to\n    de/serialize each element of the list.\n\n    :param Field cls_or_instance: A field class or instance.\n    :param str delimiter: Delimiter between values.\n    \"\"\"\n\n    default_error_messages = {\"invalid\": \"Not a valid delimited list.\"}\n\n    def __init__(\n        self,\n        cls_or_instance: ma.fields.Field | type,\n        *,\n        delimiter: str | None = None,\n        **kwargs,\n    ):\n        self.delimiter = delimiter or self.delimiter\n        super().__init__(cls_or_instance, **kwargs)\n\n\nclass DelimitedTuple(DelimitedFieldMixin, ma.fields.Tuple):\n    \"\"\"A field which is similar to a Tuple, but takes its input as a delimited\n    string (e.g. \"foo,bar,baz\").\n\n    Like Tuple, it can be given a tuple of nested field types which it will use to\n    de/serialize each element of the tuple.\n\n    :param Iterable[Field] tuple_fields: An iterable of field classes or instances.\n    :param str delimiter: Delimiter between values.\n    \"\"\"\n\n    default_error_messages = {\"invalid\": \"Not a valid delimited tuple.\"}\n\n    def __init__(\n        self,\n        tuple_fields,\n        *,\n        delimiter: str | None = None,\n        **kwargs,\n    ):\n        self.delimiter = delimiter or self.delimiter\n        super().__init__(tuple_fields, **kwargs)\n", "path": "src/webargs/fields.py"}]}