Suggestion: Improve `urlencode()` handling of boolean values

Background

urllib.parse.urlencode() currently converts boolean values using str(), resulting in query parameters like:

urlencode({'is_active': True})  # -> "is_active=True"

While technically correct in Python, this behavior is often counterintuitive in web contexts, where:
• True is usually serialized as “1” or “true”
• False as “0” or “false”

This can cause issues when interacting with APIs or backends expecting boolean values in a numeric or lowercase form.

Proposal

I would like to discuss whether it’s appropriate to:
1. Provide an optional parameter to control boolean serialization (e.g. “int”, “lower”, “str”).
2. Offer a helper or higher-level API for web-compatible serialization.

For example:

urlencode({'is_active': True}, bool_encoding="int")  # -> "is_active=1"
urlencode({'is_active': True}, bool_encoding="lower")  # -> "is_active=true"
urlencode({'is_active': True}, bool_encoding="str")  # -> "is_active=True"

or

urlencode_websafe({"is_active": True}) # -> "is_active=1"

Rationale

Many web frameworks (e.g. Django, JavaScript, requests) already use 1/0 or true/false for boolean representation in query strings. Adding this flexibility to urlencode() would improve developer experience without breaking existing behavior.

Thanks.

1 Like

I think option 1 is the better route. Option 2 gives precedence to one particular encoding, and I’m not sure how we’d say 0/1 is the preferred encoding.

2 Likes

The proposal doesn’t seem as performant compared to simply replacing boolean values with the desired value, such as "true" or 1.

from urllib.parse import urlencode

urlencode({
    'is_active': "true" if True else "false"
})

I think maybe the dictionary, shown by @kousukekikuchi as a literal, is in practice a variable from outside the scope of the caller of urlencode.

This feels a bit niche: one data type, and a fixed set of transformations identified by string name. My first thought was that this would grow: someone else wants 'Y' or 'N' as the encoding, or lowercase of that, or not in English. So accepting a function here might be good:

params = {'is_active': True}
# ...
urlencode(params, bool_encoding=int)  # -> "is_active=1"
urlencode(params, bool_encoding=str)  # -> "is_active=True"
urlencode(params, bool_encoding=lambda x: str(x).lower())  # -> "is_active=true"

And then another client needs to pass floats, but limited to a given precision, or ints, but in hex, and now it looks better to have a helper function you write to normalise params for a given API, before you give it to urlencode.

+1 to what Jeff said — this kind of normalization seems a bit outside the scope of urlencode, and personally, I think keeping concerns separate is the better approach here.

1 Like