def test_queryparams_are_hashable(): params = ( httpx.QueryParams("a=123"), httpx.QueryParams({"a": 123}), httpx.QueryParams("b=456"), httpx.QueryParams({"b": 456}), ) assert len(set(params)) == 2
def test_url_params(): url = httpx.URL("https://example.org:123/path/to/somewhere", params={"a": "123"}) assert str(url) == "https://example.org:123/path/to/somewhere?a=123" assert url.params == httpx.QueryParams({"a": "123"}) url = httpx.URL("https://example.org:123/path/to/somewhere?b=456", params={"a": "123"}) assert str(url) == "https://example.org:123/path/to/somewhere?a=123" assert url.params == httpx.QueryParams({"a": "123"})
async def search_address(client: httpx.AsyncClient, postcode: str) -> List[AddressEntry]: params = httpx.QueryParams({"cmd": "postcodefull", "postcode": postcode}) params = _append_credentials_params(params) response = await _get(client=client, path="/GTP034.php", params=params) data = PosttagPostcodeFullResponse(**response) return [AddressEntry.from_response_data(data=d) for d in data.data]
def test_queryparam_setters(): q = httpx.QueryParams({"a": 1}) q.update([]) assert str(q) == "a=1" q = httpx.QueryParams([("a", 1), ("a", 2)]) q["a"] = "3" assert str(q) == "a=3" q = httpx.QueryParams([("a", 1), ("b", 1)]) u = httpx.QueryParams([("b", 2), ("b", 3)]) q.update(u) assert str(q) == "a=1&b=2&b=3" assert q["b"] == u["b"]
def _parse_practice_id(rdv_site_web: str): # Doctolib fetches multiple vaccination centers sometimes # so if a practice id is present in query, only related agendas # will be selected. params = httpx.QueryParams(httpx.URL(rdv_site_web).query) if 'pid' not in params: return None # QueryParams({'pid': 'practice-164984'}) -> 'practice-164984' # /!\ Some URL query strings look like this: # 1) ...?pid=practice-162589&?speciality_id=5494&enable_cookies_consent=1 # 2) ...?pid=practice-162589?speciality_id=5494&enable_cookies_consent=1 # Notice the weird &?speciality_id or ?speciality_id. # Case 1) is handled correctly by `httpx.QueryParams`: in that # case, 'pid' contains 'practice-164984'. # Case 2), 'pid' contains 'pid=practice-162589?speciality_id=5494' # which must be handled manually. pid = params.get('pid') if pid is None: return None try: # -> '164984' pid = pid.split('-')[-1] # May be '164984?specialty=13' due to a weird format, drop everything after '?' pid, _, _ = pid.partition('?') # -> 164984 return [int(pid)] except (ValueError, TypeError, IndexError): logger.error(f'failed to parse practice ID: {pid=}') return None
def parse(self, request: RequestTypes) -> httpx.QueryParams: if isinstance(request, httpx.Request): query = request.url.query else: _, url, *_ = request query = httpx.URL(url).query return httpx.QueryParams(query) # TODO: Cache params on request?
def get_url(self, path, **params): url = httpx.URL( "https://{host}:{port}/".format(host=self.config.server_host, port=self.config.server_port), False, httpx.QueryParams(params), ) url = url.join(path) return str(url)
def app(request: httpx.Request) -> httpx.Response: assert request.url.path == "/api/Organizations/getConsultationReasons" assert dict(httpx.QueryParams(request.url.query)) == { "params": json.dumps({ "organizationId": 159, "doctorId": 216 }) } path = Path("tests/fixtures/avecmondoc/get_reasons.json") return httpx.Response(200, json=json.loads(path.read_text(encoding='utf8')))
def app(request: httpx.Request) -> httpx.Response: assert "User-Agent" in request.headers if request.url.path == "/booking/centre1.json": path = Path("tests", "fixtures", "doctolib", "basic-booking.json") return httpx.Response(200, json=json.loads(path.read_text(encoding="utf-8"))) assert request.url.path == "/availabilities.json" params = dict(httpx.QueryParams(request.url.query)) path = Path("tests", "fixtures", "doctolib", "basic-availabilities.json") return httpx.Response(200, json=json.loads(path.read_text(encoding="utf-8")))
def app(request: httpx.Request) -> httpx.Response: assert request.url.path == "/v1/public/search" assert dict(httpx.QueryParams(request.url.query)) == { "page": "1", "per_page": "10000", "in.isPublicProfile": "true", "in.isCovidVaccineSupported": "true", "or.covidOnlineBookingAvailabilities.Vaccination AstraZeneca": "true", "or.covidOnlineBookingAvailabilities.Vaccination Pfizer": "true", } path = Path("tests/fixtures/ordoclic/search.json") return httpx.Response(200, json=json.loads(path.read_text()))
def app(request: httpx.Request) -> httpx.Response: assert request.url.path == '/v1/public/search' assert dict(httpx.QueryParams(request.url.query)) == { 'page': '1', 'per_page': '10000', 'in.isPublicProfile': 'true', 'in.isCovidVaccineSupported': 'true', 'or.covidOnlineBookingAvailabilities.Vaccination AstraZeneca': 'true', 'or.covidOnlineBookingAvailabilities.Vaccination Pfizer': 'true', } path = Path('tests/fixtures/ordoclic/search.json') return httpx.Response(200, json=json.loads(path.read_text()))
def app(request: httpx.Request) -> httpx.Response: assert request.url.path == "/api/Doctors/search" assert dict(httpx.QueryParams(request.url.query)) == { "params": json.dumps({ "city": None, "gps": None, "dateBefore": None }), "options": json.dumps({ "limit": 1000, "page": 1, "distance": None }) } path = Path("tests/fixtures/avecmondoc/search-result.json") return httpx.Response(200, json=json.loads(path.read_text(encoding='utf8')))
def app(request: httpx.Request) -> httpx.Response: assert "User-Agent" in request.headers if request.url.path == "/booking/centre1.json": path = Path("tests", "fixtures", "doctolib", "basic-booking.json") return httpx.Response(403, text="Anti dDos") assert request.url.path == "/availabilities.json" params = dict(httpx.QueryParams(request.url.query)) assert params == { "start_date": start_date, "visit_motive_ids": "2", "agenda_ids": "3", "insurance_sector": "public", "practice_ids": "4", "destroy_temporary": "true", "limit": str(DOCTOLIB_CONF.pagination["pages"]), } path = Path("tests", "fixtures", "doctolib", "basic-availabilities.json") return httpx.Response(200, json=json.loads(path.read_text(encoding="utf-8")))
def app(request: httpx.Request) -> httpx.Response: assert "X-Covid-Tracker-Key" in request.headers if request.url.path == "/booking/centre1.json": path = Path("tests", "fixtures", "doctolib", "basic-booking.json") return httpx.Response(200, json=json.loads(path.read_text())) assert request.url.path == "/availabilities.json" params = dict(httpx.QueryParams(request.url.query)) assert params == { "start_date": start_date, "visit_motive_ids": "2", "agenda_ids": "3", "insurance_sector": "public", "practice_ids": "4", "destroy_temporary": "true", "limit": str(DOCTOLIB_SLOT_LIMIT), } path = Path("tests", "fixtures", "doctolib", "basic-availabilities.json") return httpx.Response(200, json=json.loads(path.read_text()))
def test_queryparam_types(): q = httpx.QueryParams(None) assert str(q) == "" q = httpx.QueryParams({"a": True}) assert str(q) == "a=true" q = httpx.QueryParams({"a": False}) assert str(q) == "a=false" q = httpx.QueryParams({"a": ""}) assert str(q) == "a=" q = httpx.QueryParams({"a": None}) assert str(q) == "a=" q = httpx.QueryParams({"a": 1.23}) assert str(q) == "a=1.23" q = httpx.QueryParams({"a": 123}) assert str(q) == "a=123" q = httpx.QueryParams({"a": [1, 2]}) assert str(q) == "a=1&a=2"
def _build_query(self, **kwargs): return httpx.QueryParams(token=self.token, **kwargs)
def redirects(request: httpx.Request) -> httpx.Response: if request.url.scheme not in ("http", "https"): raise httpcore.UnsupportedProtocol( f"Scheme {request.url.scheme!r} not supported.") if request.url.path == "/no_redirect": return httpx.Response(200) elif request.url.path == "/redirect_301": status_code = httpx.codes.MOVED_PERMANENTLY content = b"<a href='https://example.org/'>here</a>" headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers, content=content) elif request.url.path == "/redirect_302": status_code = httpx.codes.FOUND headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_303": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/relative_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/malformed_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://:443/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/invalid_redirect": status_code = httpx.codes.SEE_OTHER raw_headers = [(b"location", "https://😇/".encode("utf-8"))] return httpx.Response(status_code, headers=raw_headers) elif request.url.path == "/no_scheme_redirect": status_code = httpx.codes.SEE_OTHER headers = {"location": "//example.org/"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/multiple_redirects": params = httpx.QueryParams(request.url.query) count = int(params.get("count", "0")) redirect_count = count - 1 status_code = httpx.codes.SEE_OTHER if count else httpx.codes.OK if count: location = "/multiple_redirects" if redirect_count: location += f"?count={redirect_count}" headers = {"location": location} else: headers = {} return httpx.Response(status_code, headers=headers) if request.url.path == "/redirect_loop": status_code = httpx.codes.SEE_OTHER headers = {"location": "/redirect_loop"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/cross_domain": status_code = httpx.codes.SEE_OTHER headers = {"location": "https://example.org/cross_domain_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/cross_domain_target": status_code = httpx.codes.OK data = { "body": request.content.decode("ascii"), "headers": dict(request.headers), } return httpx.Response(status_code, json=data) elif request.url.path == "/redirect_body": status_code = httpx.codes.PERMANENT_REDIRECT headers = {"location": "/redirect_body_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_no_body": status_code = httpx.codes.SEE_OTHER headers = {"location": "/redirect_body_target"} return httpx.Response(status_code, headers=headers) elif request.url.path == "/redirect_body_target": data = { "body": request.content.decode("ascii"), "headers": dict(request.headers), } return httpx.Response(200, json=data) elif request.url.path == "/cross_subdomain": if request.headers["Host"] != "www.example.org": status_code = httpx.codes.PERMANENT_REDIRECT headers = {"location": "https://www.example.org/cross_subdomain"} return httpx.Response(status_code, headers=headers) else: return httpx.Response(200, text="Hello, world!") elif request.url.path == "/redirect_custom_scheme": status_code = httpx.codes.MOVED_PERMANENTLY headers = {"location": "market://details?id=42"} return httpx.Response(status_code, headers=headers) if request.method == "HEAD": return httpx.Response(200) return httpx.Response(200, html="<html><body>Hello, world!</body></html>")
def test_queryparam_add(): q = httpx.QueryParams("a=123") q = q.add("a", "456") assert q == httpx.QueryParams("a=123&a=456")
def test_queryparams(source): q = httpx.QueryParams(source) assert "a" in q assert "A" not in q assert "c" not in q assert q["a"] == "456" assert q.get("a") == "456" assert q.get("nope", default=None) is None assert q.get_list("a") == ["123", "456"] assert list(q.keys()) == ["a", "b"] assert list(q.values()) == ["456", "789"] assert list(q.items()) == [("a", "456"), ("b", "789")] assert len(q) == 2 assert list(q) == ["a", "b"] assert dict(q) == {"a": "456", "b": "789"} assert str(q) == "a=123&a=456&b=789" assert repr(q) == "QueryParams('a=123&a=456&b=789')" assert httpx.QueryParams({ "a": "123", "b": "456" }) == httpx.QueryParams([("a", "123"), ("b", "456")]) assert httpx.QueryParams({ "a": "123", "b": "456" }) == httpx.QueryParams("a=123&b=456") assert httpx.QueryParams({ "a": "123", "b": "456" }) == httpx.QueryParams({ "b": "456", "a": "123" }) assert httpx.QueryParams() == httpx.QueryParams({}) assert httpx.QueryParams([("a", "123"), ("a", "456") ]) == httpx.QueryParams("a=123&a=456") assert httpx.QueryParams({"a": "123", "b": "456"}) != "invalid" q = httpx.QueryParams([("a", "123"), ("a", "456")]) assert httpx.QueryParams(q) == q
def test_queryparam_setter_is_hard_deprecated(): q = httpx.QueryParams("a=123") with pytest.raises(RuntimeError): q["a"] = "456"
def test_queryparam_set(): q = httpx.QueryParams("a=123") q = q.set("a", "456") assert q == httpx.QueryParams("a=456")
def test_queryparam_merge(): q = httpx.QueryParams("a=123") q = q.merge({"b": "456"}) assert q == httpx.QueryParams("a=123&b=456") q = q.merge({"a": "000", "c": "789"}) assert q == httpx.QueryParams("a=000&b=456&c=789")
def parse(self, request: httpx.Request) -> httpx.QueryParams: query = request.url.query return httpx.QueryParams(query)
def clean(self, value: QueryParamTypes) -> httpx.QueryParams: return httpx.QueryParams(value)
def test_queryparam_remove(): q = httpx.QueryParams("a=123") q = q.remove("a") assert q == httpx.QueryParams("")
def test_queryparam_update_is_hard_deprecated(): q = httpx.QueryParams("a=123") with pytest.raises(RuntimeError): q.update({"a": "456"})