yarl icon indicating copy to clipboard operation
yarl copied to clipboard

Support building query without values

Open rominf opened this issue 5 years ago • 4 comments

Sometimes query consists not only of pairs of keys and values but also can contain standalone keys. In my opinion, it's bad, but I encountered this a few times in different APIs. For example, I need to build a URL like: https://api.crowdin.com/api/project/{project_identifier}/pre-translate?key={project_key}&json (see https://support.crowdin.com/api/pre-translate/ for details).

I propose to make an interface look like this:

url = URL('https://api.crowdin.com/api/project/{project-identifier}/pre-translate')
url = url.with_query(key=project_key, json=None)

This is safe because now passing None as keyword argument value raises TypeError. If user wants to get an URL with json= (note that the last symbol is the equals sign), he passes json='' as he does now.

rominf avatar May 08 '19 10:05 rominf

Right now I'm inclining to agree with your proposal but want to check it again for controversy. I don't like usage of None as not for None value but the absence of value at all. On the other hand, I don't see a better solution.

Would you prepare a PR for review?

asvetlov avatar May 08 '19 13:05 asvetlov

@asvetlov it could be a special constant:

# in yarl:
QUERY_KEY_WITHOUT_VALUE = object()

# in code:
from yarl import QUERY_KEY_WITHOUT_VALUE
url = URL('https://api.crowdin.com/api/project/{project-identifier}/pre-translate')
url = url.with_query(key=project_key, json=QUERY_KEY_WITHOUT_VALUE)

It's more obvious, but IMHO more ugly.

I can prepare a PR tomorrow.

rominf avatar May 08 '19 14:05 rominf

Agree, a constant is worse than just None

asvetlov avatar May 08 '19 14:05 asvetlov

urllib.parse.parse_qsl() does not distinguish empty value from absent value:

>>> parse_qsl('a=b&c=', keep_blank_values=True)
[('a', 'b'), ('c', '')]
>>> parse_qsl('a=b&c', keep_blank_values=True)
[('a', 'b'), ('c', '')]

As well as it does not distinguish the & separator from the ; separator.

>>> parse_qsl('a=b&c=d')
[('a', 'b'), ('c', 'd')]
>>> parse_qsl('a=b;c=d')
[('a', 'b'), ('c', 'd')]

If you want to create a query with non-standard representation (parameters without =, with the ; separator instead of &, or %-encoded characters which do not need to be encoded in norm, or using encoding different from UTF-8), you need to change directly the query_string or even the raw_query_string attributes, but not the query dict.

url = url.with_query(key=project_key)
url = url.with_query(url.query_string + '&json')

serhiy-storchaka avatar Sep 27 '20 09:09 serhiy-storchaka