'params' argument does not accept custom query string

Hello,

I'm evaluating `requests` as a building block to develop a client that needs to query a third party web service. I like the succinctness of the API so I would like to be able to use it. However, I'm facing the problem below.

I need to be able to send requests using URLs of the form below:

https://www.example.com/?versioning

Please note that the query string of the URL is not of the type 'key=value' but just 'key'.

In the `requests` API documentation, it is stated about the `params` argument of the constructor or class `requests.Request`:

_params = None_
_Dictionary or **byte** of querystring data to attach to the Request._

However, if I pass a `bytes`argument I get an exception:

`ValueError: cannot encode objects that are not 2-tuples`

Looking at the source code (file utils.py:134) one can observe that `requests` does not accept bytes (nor string, nor bool, nor int, BTW). So I suspect, there is a bug in the documentation.

What is the recommended way for using `requests` for interacting with a web service with a URL which needs a "custom" query string (e.g. not of the form "key=value")? Should I encode the query string in my application and pass it as part of the `url` argument and not as part of `params`argument?

[Test context: Python v3.3, MacOS X v10.7.5]

Thanks of your help.


The docs need to be updated, sorry for the trouble. You should be adding the string directly to the URL that you're passing in.


I think this can be closed now. See: https://github.com/kennethreitz/requests/commit/50877717c810d47e90281c85ecce198c08af40a5


Good spot. Resolved by #940.


I think passing params without values should be supported by `params` parameter. Currently setting param's value to `None` in `params` dict makes Requests skip adding the param to a query string. I propose to change this behavior so that in such case param is added without value. @kennethreitz What do you think?


@piotr-dobrogost what about the following case:

``` python
from requests import session


s = session(params={'param1': 'default1', 'param2': 'default2'})

# perform requests with the default params
# but now I don't want param1 set

response = s.get(url, params={'param1': None})
response.request.url
# '...?param2=default2'
```

With your proposed change, `response.request.url == '...?param1&param2=default2''`. Perhaps we could use `''` an empty string instead (if in fact we're realistically considering this)?


Right, I forgot about this scenario. Related issue #378

@airnandez
Would `https://www.example.com/?versioning=` work in your case?


@piotr-dobrogost I meant implement `''` as a special-case that wouldn't add the `=`


According to 4.10.22.5 URL-encoded form data, point 4.10 from HTML5 working draft specification (http://www.w3.org/TR/2012/WD-html5-20120329/) `=` character must be present. Thus special-casing `''` to mean skip `=` is out of question as this is the most natural way to add a form field without value to query string in a conforming way. As you noticed `None` is used as a marker to remove such a field entirely from query string. We would have to introduce some magic token like `SKIP_VALUE` for value to designate skipping value **and** the `=` sign. It's not super nice API so the question still remains how often `key` alone is supported on the server side but `key=`, which is both conforming and supported by Requests is not?


@piotr-dobrogost I love how you seem to find the specifications when they're needed.


Thanks. I guess it comes from the fact this often seems easier than writing code :\ However in this case HTML specification doesn't help this much as it pertains only to data from html `<form>` element being sent in the query string. Generally query string can be of any form as long as it conforms to constraints laid out in section [3.4 Query](http://tools.ietf.org/html/rfc3986#section-3.4) of URI specification. That's why I would base introduction of support for _free form_ query string based on real world usage of such query strings.


Feels like this discussion is a bit separate from the original issue here (plus it keeps falling off my radar because it's in a closed issue). Want to open a new one for this feature suggestion?


> Feels like this discussion is a bit separate from the original issue (...)

Not even slightly :) We could and should continue here. If you could reopen, please do.


Re-read the original post, you're quite right. =D


But yeah, Requests does conform to the spec (proof for anyone who's wary to believe me and @piotr-dobrogost):

``` python
import requests

r = requests.get('http://httpbin.org/', params={'foo': ''})
assert r.request.full_url == 'http://httpbin.org/?foo='
```


Hello, OP here,

@piotr-dobrogost: "Would https://www.example.com/?versioning= work in your case?"
Unfortunately not. The REST APIs I need to use requires the query string to have the form `?versioning` or `?acl` (i.e. without '=' and without value). 

@piotr-dobrogost: "That's why I would base introduction of support for free form query string based on real world usage of such query strings."
See for instance the [Google storage API](https://developers.google.com/storage/docs/reference-methods) or [Amazon S3 REST API](http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETversioningStatus.html).
I just tested against Amazon S3 server and can confirm that a query string of the form `?versioning=` is not accepted.

From the `Requests` user point of view, it would be nice to be able to express the contents of the query string in the `params` argument. From this exchange, I understand that associating a value of `None` or `''` (empty string) to a key in the params dict is not a viable solution. I would therefore suggest to allow for `Requests` users to provide the pre-formatted query string in the `params` argument. This user-provided query string should be non URL-encoded, for instance:

```
    import requests
    r = requests.get('http://httpbin.org/', params='versioning&response=json')
    assert r.request.full_url == 'http://httpbin.org/?versioning&response=json'
```

I think this way of doing would be cleaner for the user than to embed the query string in the URL (even if this works in my particular case) and not to use the `params` argument. However, I acknowledge that this is very subjective.


> it would be nice to be able to express the contents of the query string in the `params` argument

Totally agree. It's misleading at best to have to embed query string into url in spite of the fact there's `params` param. Making `params` support pre-formatted query string goes in right direction. However having one parameter for both makes it impossible to have both at the same time. I would suggest adding new parameter `query_string` and combining contents of it with contents of `params` and the query string part of the url itself.
@kennethreitz Is this ok?


No. The current behavior is proper and was well thought out.