Batu Lab NotesPractical developer guides

Preserve repeated query keys with parse_qsl

By Batu · English technical notes

Also published in our primary archive.

Repeated query keys are meaningful in many URL conventions. For example, a fixture URL may use two tag fields to represent two selected tags. parse_qs() groups values by key, which is useful when lookup is the goal, but it returns a dictionary and does not express the original sequence as a list of individual fields. Use parse_qsl() when the ordered sequence of name/value pairs is the data you need.

The example first isolates the query with urlsplit(), then calls parse_qsl(). Its resulting list contains both tag pairs in their input order, followed by sort=name. The assertion compares the full list, so it verifies that this fixture retained the duplicate key and its ordering. Printing the list provides deterministic output suitable for a small parsing check.

parse_qsl() decodes form-style query data: a plus sign represents a space and percent escapes are decoded using UTF-8 by default. By default, blank fields are discarded; pass keep_blank_values=True when blanks must be represented. Its max_num_fields parameter, added in Python 3.8, can set a field-count limit for application input. This example uses APIs available in Python 3.2+ and does not depend on that newer option. Consult the official urllib.parse documentation.

AI assistance disclosure: this article was drafted with AI assistance and should be reviewed against the target protocol’s query conventions.

from urllib.parse import parse_qsl, urlsplit

fixture_url = "https://fixtures.example.test/items?tag=python&tag=testing&sort=name"
pairs = parse_qsl(urlsplit(fixture_url).query, keep_blank_values=True)

assert pairs == [
    ("tag", "python"),
    ("tag", "testing"),
    ("sort", "name"),
]

print(pairs)
[('tag', 'python'), ('tag', 'testing'), ('sort', 'name')]