master.Unable to compare two httpx.URL() objects, which have the same query params but with different order
url1 = httpx.URL('http://test?a=1&b=2')
url2 = httpx.URL('http://test?b=2&a=1')
assert url1 == url2
The URLs considered as equal
Obviously same URLs considered as different
Traceback (most recent call last):
File "/Users/user/.virtualenvs/some/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3417, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-11-9666aec6e8b1>", line 4, in <module>
assert url1 == url2
AssertionError
Given issue makes writing test quite annoying. It forces saving same order in the query string. It also hard to mock such calls.
Got the same issue, the things will go much easier if it's fixed. Hope it will be done soon.
Thanks for reporting @devova.
So, this isn't as obvious a behaviour as you might think.
Query string ordering generally won't be important, but it can be, and we can't really treat those URLs as being unambiguously equivalent.
I took a look through a few other libraries here too...
>>> from hyperlink import URL
>>> URL.from_text('http://test?a=1&b=2') == URL.from_text('http://test?b=2&a=1')
False
>>> import urllib3
>>> urllib3.util.parse_url('http://test?a=1&b=2') == urllib3.util.parse_url('http://test?b=2&a=1')
False
>>> from furl import furl
>>> furl('http://test?a=1&b=2') == furl('http://test?b=2&a=1')
False
The best you can do for your use case at the moment is check for equivalence of parsed query parameter datastructures...
httpx.QueryParams(url1.query) == httpx.QueryParams(url2.query)
That's not to say that we couldn't consider changing our equality behaviour. It might be more practical, although it's a bit fuzzy.
Also useful might be having a .params property on the class, to easily access the parsed query params datastructure, so eg...
url1.params == url2.params
Also, if folks could not do the "let's get a bunch of tictrac folks to upvote this" thing, that'd be grand. 🤣
There are more productive ways to push things forward... https://github.com/sponsors/encode 💚
Tweaked the issue title on this one.
Similar issue here would be should URL escaping be relevant when comparing URLs...
# What behaviour would we most expect here...
httpx.URL("http://example.com/path/to%20somewhere") == httpx.URL("http://example.com/path/to somewhere")
As with the query ordering, these two URLs are not strictly equal, but to most application frameworks they probably are equivalent.
@tomchristie thank you for given examples, it there need to explicitly compare URLs these options might work. As I sad it also hard to mock the requests. I'm using pytest-httpx where I can do
httpx_mock.add_response(url=...)
And here I have to really care about query string order since under the hood it compares httpx.URL objects
Hello @devova feel free to enter the issue in pytest-httpx, I will take a look at it as order of parameters should indeed not matter if the names are different
@devova pytest-httpx 0.10.1 is now available on pypi.org and should fix this issue.
Should you encounter new issues in the future, don't hesitate to ask for help on the GitHub page for pytest-httpx.
@Colin-b very appreciate it
Super — so should we consider this resolved? I'm personally not too fond of making this too smart — although it could be that url.params could help. But the other case about escaping is messy too... And I think we should treat it as we do now: different URLs. It's easier for people to pre-process then feed into HTTPX than to get around some automatic processing we would do if you _really_ want ordering or escaping to matter.
Yeah, the solution works for me. I also agree that it was a usability problem in testing rather than implicit URL equality in main httpx library.
Thank you all for your contribution.