diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ffd38f4b..be5fe2aa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,11 @@ Other changes: Users should use ``await parser.async_parse()`` to access the async features of ``Parser``. +* *Backwards-incompatible*: `DelimitedList` and `DelimitedTuple` fields have + changed their default `empty_value` from the empty string (`""`) to `missing`. + This allows nested fields with a `load_default` to be used to better customize + behavior. + * Drop support for Python 3.9, which is EOL (:pr:`1019`). * Drop support for Bottle < 0.13 (:pr:`1019`). * Drop support for Flask < 3.1.0 (:pr:`1023`). diff --git a/src/webargs/fields.py b/src/webargs/fields.py index e03783eb..526b0777 100644 --- a/src/webargs/fields.py +++ b/src/webargs/fields.py @@ -42,8 +42,7 @@ class DelimitedFieldMixin: 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 = "" + empty_value: typing.Any = ma.missing def _serialize(self, value, attr, obj, **kwargs): # serializing will start with parent-class serialization, so that we correctly diff --git a/tests/test_core.py b/tests/test_core.py index 19aaee5a..4839a61a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -11,7 +11,6 @@ INCLUDE, RAISE, Schema, - missing, pre_load, validates_schema, ) @@ -1119,14 +1118,10 @@ class ZeroTuple(fields.DelimitedTuple): assert parsed["ids"] == (1, 0, 3) -def test_delimited_list_using_missing_for_empty(web_request, parser): - # this is "future" because we plan to make this the default for webargs v9.0 - class FutureList(fields.DelimitedList): - empty_value = missing - +def test_delimited_list_missing_element_defaults_to_load_default(web_request, parser): web_request.json = {"ids": "foo,,bar"} schema_cls = Schema.from_dict( - {"ids": FutureList(fields.String(load_default="nil"))} + {"ids": fields.DelimitedList(fields.String(load_default="nil"))} ) schema = schema_cls() @@ -1134,6 +1129,19 @@ class FutureList(fields.DelimitedList): assert parsed["ids"] == ["foo", "nil", "bar"] +def test_delimited_list_using_empty_string_for_empty(web_request, parser): + # this is "past" because it was the default for webargs v8.x + class PastList(fields.DelimitedList): + empty_value = "" + + web_request.json = {"ids": "foo,,bar"} + schema_cls = Schema.from_dict({"ids": PastList(fields.String(load_default="nil"))}) + schema = schema_cls() + + parsed = parser.parse(schema, web_request) + assert parsed["ids"] == ["foo", "", "bar"] + + def test_missing_list_argument_not_in_parsed_result(web_request, parser): # arg missing in request web_request.json = {}