Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ Features:
``__version_info__`` constants from ``__init__.py`` file. Users should use
``importlib.metadata`` instead (:pr:`1024`).

Bug fixes:

* Fix the Tornado parser treating a repeated header as a sequence of
characters: Tornado joins repeated headers into a single comma-separated
string, so loading such a header into a ``List`` field produced a list of
single-character strings. It is now loaded as a single value, matching the
other parsers (:pr:`1108`).

Other changes:


Expand Down
6 changes: 6 additions & 0 deletions src/webargs/tornadoparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def __getitem__(self, key: str) -> typing.Any:
if value is core.missing:
return core.missing
if key in self.multiple_keys:
# A str or bytes value is a single value, not a sequence of
# them: Tornado joins repeated headers into one comma-separated
# string (e.g. "a, b"), and iterating over it would yield its
# characters. Only genuine sequences are expanded.
if isinstance(value, (str, bytes)):
return [_unicode(value)]
return [
_unicode(v) if isinstance(v, (str, bytes)) else v for v in value
]
Expand Down
14 changes: 14 additions & 0 deletions tests/test_tornadoparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ def test_it_should_get_multiple_values(self):
result = parser.load_headers(request, author_schema)
assert result["works"] == ["Prometheus Bound", "Prometheus Unbound"]

def test_it_should_treat_a_repeated_header_as_one_value(self):
# Tornado collects repeated headers into a single comma-separated
# string, so a List field must see one value and not the string's
# characters.
headers = tornado.httputil.HTTPHeaders()
headers.add("name", "Euphorion")
headers.add("works", "Prometheus Bound")
headers.add("works", "Prometheus Unbound")
assert headers["works"] == "Prometheus Bound,Prometheus Unbound"

request = make_request(headers=headers)
result = parser.load_headers(request, author_schema)
assert result["works"] == ["Prometheus Bound,Prometheus Unbound"]

def test_it_should_return_missing_if_not_present(self):
request = make_request()
result = parser.load_headers(request, author_schema)
Expand Down