requests: Cannot make URL query string with a parameter without a value

URL query string may contain a parameter, which has no value i.e. http://host/path/?foo or http://host/path/?a=1&foo. Currently Requests does not provide support for that.

In [68]: d
Out[68]: {'a': 1, 'foo': None}

In [69]: tl
Out[69]: [('a', 1), ('foo',)]

In [70]: RequestEncodingMixin._encode_params(d)
Out[70]: 'a=1'

In [71]: RequestEncodingMixin._encode_params(tl)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-71-5d4dac855108> in <module>()
----> 1 RequestEncodingMixin._encode_params(tl)

/home/f557010/jpm/local/lib/python2.7/site-packages/requests/models.pyc in _encode_params(data)
     87         elif hasattr(data, '__iter__'):
     88             result = []
---> 89             for k, vs in to_key_val_list(data):
     90                 if isinstance(vs, basestring) or not hasattr(vs, '__iter__'):
     91                     vs = [vs]

ValueError: need more than 1 value to unpack

Expected:

'a=1&foo'

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 4
  • Comments: 34 (14 by maintainers)

Most upvoted comments

in 3.0, i think we can consider making emptry string not result in an =

It’s not the same as having a parameter without a value. Yours renders the = sign after the parameter name. Some applications will work, but as a matter of providing a complete solution this exact case should be addressed. That urllib doesn’t do it is of no significance. Requests does many things better than standard libraries for HTTP - that is its reason to exist.

What if we were to make a specific typed argument that could indicate to requests that the param is to be added without a value?

# defined somewhere in requests
class ValuelessParam(object):
    pass
....
....

params = {'foo': 'some_param', 'bar': requests.ValuelessParam()}

requests.get('http://something', params=params)

# url should be 'http://something?foo=some_param&bar'

Its not None, its not a scalar constant… so it should be backwards compatible. Under the hood… we could check for that value type and specially append this parameter to the constructed url.

2020… and still an issue… SMH

So I’m guessing this is still a problem?

If you have parameters that have no value, you can list them as part of the url and any additional parameters will append with & instead of starting with ?:

In [3]: r = requests.get("http://www.google.com", params={'test': 'true'})

In [4]: r.url
Out[4]: 'http://www.google.com/?test=true'

In [5]: r = requests.get("http://www.google.com?test2", params={'test': 'true'})

In [6]: r.url
Out[6]: 'http://www.google.com/?test2&test=true'

Yeah, No. I’d rather support a built-in like None, and I’m not sure that’s the best idea — but it could work well. False would not.

What’s the solution folks?

That would be a pretty major change though, and I don’t think it would benefit many people. Perhaps an empty tuple could be considered. (e.g. (,).

Unfortunately facing the same issue. Wouldn’t it be a better pattern or option to be able to inject/pass an optional custom formatter/encoder or a specific flag for handling None behaviour?

@Lukasa You seem to know the codebase better than most of us, what do you think about it?

Wouldn’t that resolve in a non-breaking-change, @kennethreitz? Defaults could be set to mimic 2.x behaviour.

None won’t work either: the codebase already gives it the meaning of "unset the value set at the Session level.

So while that will definitely work, I don’t think that API will get past Kenneth.

Here is a crazy thought:

>>> import requests
>>> r = requests.get('https://httpbin.org/get', params={'foo': None})
>>> r.request.url
'https://httpbin.org/get?foo'

That is what I think should be happening.

What is actually happening:

'https://httpbin.org/get'

😦