Skip to content

Commit e0e5673

Browse files
committed
📝 document qs 6.15 parse parity updates
1 parent e4e8352 commit e0e5673

4 files changed

Lines changed: 48 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 1.5.1-dev
22

3+
* [FEAT] add `DecodeOptions.strict_merge` for Node `qs` 6.15 `strictMerge` parity
4+
* [FIX] align `decode` `list_limit` semantics with Node `qs` `arrayLimit` as a maximum element count
5+
* [FIX] combine bracket-array duplicate assignments regardless of `DecodeOptions.duplicates`
36
* [FIX] align `decode` with Node `qs` 6.15.2 by normalizing dotted keys before preserving `depth=0` input
47
* [FIX] align `encode` with Node `qs` 6.15.2 by using the configured delimiter after `charset_sentinel`
58

docs/README.rst

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,38 @@ change the behavior when duplicate keys are encountered
196196
qs.DecodeOptions(duplicates=qs.Duplicates.LAST),
197197
) == {'foo': 'baz'}
198198
199+
Bracket-array keys always combine, regardless of the duplicate strategy:
200+
201+
.. code:: python
202+
203+
import qs_codec as qs
204+
205+
assert qs.decode(
206+
'a=1&a=2&b[]=1&b[]=2',
207+
qs.DecodeOptions(duplicates=qs.Duplicates.LAST),
208+
) == {'a': '2', 'b': ['1', '2']}
209+
210+
When a key appears as both an object and a scalar,
211+
:py:attr:`strict_merge <qs_codec.models.decode_options.DecodeOptions.strict_merge>` wraps the conflicting values in a
212+
``list`` by default:
213+
214+
.. code:: python
215+
216+
import qs_codec as qs
217+
218+
assert qs.decode('a[b]=c&a=d') == {'a': [{'b': 'c'}, 'd']}
219+
220+
Set ``strict_merge`` to ``False`` to restore the legacy behavior, where non-empty string scalars become object keys:
221+
222+
.. code:: python
223+
224+
import qs_codec as qs
225+
226+
assert qs.decode(
227+
'a[b]=c&a=d',
228+
qs.DecodeOptions(strict_merge=False),
229+
) == {'a': {'b': 'c', 'd': True}}
230+
199231
If you have to deal with legacy browsers or services, there’s also
200232
support for decoding percent-encoded octets as :py:attr:`LATIN1 <qs_codec.enums.charset.Charset.LATIN1>`:
201233

@@ -310,11 +342,11 @@ Note that an empty ``str``\ing is also a value and will be preserved:
310342
assert qs.decode('a[0]=b&a[1]=&a[2]=c') == {'a': ['b', '', 'c']}
311343
312344
:py:attr:`decode <qs_codec.decode>` will also limit specifying indices
313-
in a ``list`` to a maximum index of ``20``. Any ``list`` members with an
314-
index of greater than ``20`` will instead be converted to a ``dict`` with
315-
the index as the key. This is needed to handle cases when someone sent,
316-
for example, ``a[999999999]`` and it will take significant time to iterate
317-
over this huge ``list``.
345+
in a ``list`` to a maximum element count of ``20``. Index ``19`` is the
346+
last index that can create a default ``list``; index ``20`` and higher
347+
are converted to a ``dict`` with the index as the key. This is needed to
348+
handle cases when someone sent, for example, ``a[999999999]`` and it
349+
would take significant time to iterate over this huge ``list``.
318350

319351
.. code:: python
320352

src/qs_codec/models/decode_options.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ class DecodeOptions:
4747
limit, index ``19`` is the last index that can create a list; index ``20`` already
4848
overflows to a ``dict``.
4949
50-
This limit also applies to comma–split lists when ``comma=True``. Set a larger value if
51-
you explicitly need more items, or set a smaller one to harden against abuse.
50+
This limit also applies to decoded list growth from comma-split values when ``comma=True``.
51+
For bracket-array assignments such as ``foo[]=1,2,3``, the comma-split payload is wrapped
52+
as a single outer list element, so the inner payload may contain more values than
53+
``list_limit`` while still respecting the outer container limit.
5254
"""
5355

5456
charset: Charset = Charset.UTF8

tests/unit/example_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ def test_lists(self):
134134
assert qs_codec.decode("a[]=&a[]=b") == {"a": ["", "b"]}
135135
assert qs_codec.decode("a[0]=b&a[1]=&a[2]=c") == {"a": ["b", "", "c"]}
136136

137-
# **qs_codec** will also limit specifying indices in a `list` to a maximum index of `20`.
138-
# Any `list` members with an index of greater than `20` will instead be converted to a `dict` with the index as
139-
# the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take
140-
# significant time to iterate over this huge `list`.
137+
# **qs_codec** will also limit specifying indices in a `list` to a maximum element count of `20`.
138+
# Index `19` is the last index that can create a default `list`; index `20` and higher are converted to a
139+
# `dict` with the index as the key. This is needed to handle cases when someone sent, for example,
140+
# `a[999999999]` and it will take significant time to iterate over this huge `list`.
141141
assert qs_codec.decode("a[100]=b") == {"a": {"100": "b"}}
142142

143143
# This limit can be overridden by passing an `DecodeOptions.list_limit` option:

0 commit comments

Comments
 (0)