Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/default_to_httpx2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
default: major
---

# Default to using httpx2 instead of httpx causing the following breaking API changes:
- `set_httpx_client` -> `set_httpx2_client`
- `get_httpx_client` -> `get_httpx2_client`
- `set_async_httpx_client` -> `set_async_httpx2_client`
- `get_async_httpx_client` -> `get_async_httpx2_client`
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from unittest.mock import MagicMock

import httpx
import httpx2

from end_to_end_tests.functional_tests.helpers import (
with_generated_client_fixture,
Expand Down Expand Up @@ -44,44 +44,44 @@ class TestPathParameterEncoding:

def test_path_params_with_normal_chars_work(self, sync_detailed, Client):
"""Test that normal alphanumeric path parameters still work correctly"""
mock_httpx_client = MagicMock(spec=httpx.Client)
mock_response = MagicMock(spec=httpx.Response)
mock_httpx2_client = MagicMock(spec=httpx2.Client)
mock_response = MagicMock(spec=httpx2.Response)
mock_response.status_code = 200
mock_response.json.return_value = {"id": "test"}
mock_response.content = b'{"id": "test"}'
mock_response.headers = {}
mock_httpx_client.request.return_value = mock_response
mock_httpx2_client.request.return_value = mock_response

client = Client(base_url="https://api.example.com")
client.set_httpx_client(mock_httpx_client)
client.set_httpx2_client(mock_httpx2_client)

sync_detailed(
item_id="item123",
detail_id="detail456",
client=client,
)

mock_httpx_client.request.assert_called_once()
call_kwargs = mock_httpx_client.request.call_args[1]
mock_httpx2_client.request.assert_called_once()
call_kwargs = mock_httpx2_client.request.call_args[1]

# Normal characters should remain unchanged
expected_url = "/items/item123/details/detail456"
assert call_kwargs["url"] == expected_url

def test_path_params_with_reserved_chars_are_encoded(self, sync_detailed, Client):
"""Test that path parameters with reserved characters are properly URL-encoded"""
# Create a mock httpx client
mock_httpx_client = MagicMock(spec=httpx.Client)
mock_response = MagicMock(spec=httpx.Response)
# Create a mock httpx2 client
mock_httpx2_client = MagicMock(spec=httpx2.Client)
mock_response = MagicMock(spec=httpx2.Response)
mock_response.status_code = 200
mock_response.json.return_value = {"id": "test"}
mock_response.content = b'{"id": "test"}'
mock_response.headers = {}
mock_httpx_client.request.return_value = mock_response
mock_httpx2_client.request.return_value = mock_response

# Create a client with the mock httpx client
# Create a client with the mock httpx2 client
client = Client(base_url="https://api.example.com")
client.set_httpx_client(mock_httpx_client)
client.set_httpx2_client(mock_httpx2_client)

# Call the endpoint with path parameters containing reserved characters
sync_detailed(
Expand All @@ -91,60 +91,60 @@ def test_path_params_with_reserved_chars_are_encoded(self, sync_detailed, Client
)

# Verify the request was made with properly encoded URL
mock_httpx_client.request.assert_called_once()
call_kwargs = mock_httpx_client.request.call_args[1]
mock_httpx2_client.request.assert_called_once()
call_kwargs = mock_httpx2_client.request.call_args[1]

# The URL should have encoded slashes and query characters
expected_url = "/items/item%2Fwith%2Fslashes/details/detail%3Fwith%3Dquery%26chars"
assert call_kwargs["url"] == expected_url

def test_path_params_with_spaces_are_encoded(self, sync_detailed, Client):
"""Test that path parameters with spaces are properly URL-encoded"""
mock_httpx_client = MagicMock(spec=httpx.Client)
mock_response = MagicMock(spec=httpx.Response)
mock_httpx2_client = MagicMock(spec=httpx2.Client)
mock_response = MagicMock(spec=httpx2.Response)
mock_response.status_code = 200
mock_response.json.return_value = {"id": "test"}
mock_response.content = b'{"id": "test"}'
mock_response.headers = {}
mock_httpx_client.request.return_value = mock_response
mock_httpx2_client.request.return_value = mock_response

client = Client(base_url="https://api.example.com")
client.set_httpx_client(mock_httpx_client)
client.set_httpx2_client(mock_httpx2_client)

sync_detailed(
item_id="item with spaces",
detail_id="detail with spaces",
client=client,
)

mock_httpx_client.request.assert_called_once()
call_kwargs = mock_httpx_client.request.call_args[1]
mock_httpx2_client.request.assert_called_once()
call_kwargs = mock_httpx2_client.request.call_args[1]

# Spaces should be encoded as %20
expected_url = "/items/item%20with%20spaces/details/detail%20with%20spaces"
assert call_kwargs["url"] == expected_url

def test_path_params_with_hash_are_encoded(self, sync_detailed, Client):
"""Test that path parameters with hash/fragment characters are properly URL-encoded"""
mock_httpx_client = MagicMock(spec=httpx.Client)
mock_response = MagicMock(spec=httpx.Response)
mock_httpx2_client = MagicMock(spec=httpx2.Client)
mock_response = MagicMock(spec=httpx2.Response)
mock_response.status_code = 200
mock_response.json.return_value = {"id": "test"}
mock_response.content = b'{"id": "test"}'
mock_response.headers = {}
mock_httpx_client.request.return_value = mock_response
mock_httpx2_client.request.return_value = mock_response

client = Client(base_url="https://api.example.com")
client.set_httpx_client(mock_httpx_client)
client.set_httpx2_client(mock_httpx2_client)

sync_detailed(
item_id="item#1",
detail_id="detail#id",
client=client,
)

mock_httpx_client.request.assert_called_once()
call_kwargs = mock_httpx_client.request.call_args[1]
mock_httpx2_client.request.assert_called_once()
call_kwargs = mock_httpx2_client.request.call_args[1]

# Hash should be encoded as %23
expected_url = "/items/item%231/details/detail%23id"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Things to know:

## Advanced customizations

There are more settings on the generated `Client` class which let you control more runtime behavior, check out the docstring on that class for more info. You can also customize the underlying `httpx.Client` or `httpx.AsyncClient` (depending on your use-case):
There are more settings on the generated `Client` class which let you control more runtime behavior, check out the docstring on that class for more info. You can also customize the underlying `httpx2.Client` or `httpx2.AsyncClient` (depending on your use-case):

```python
from my_test_api_client import Client
Expand All @@ -90,23 +90,23 @@ def log_response(response):

client = Client(
base_url="https://api.example.com",
httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
httpx2_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
)

# Or get the underlying httpx client to modify directly with client.get_httpx_client() or client.get_async_httpx_client()
# Or get the underlying httpx2 client to modify directly with client.get_httpx2_client() or client.get_async_httpx2_client()
```

You can even set the httpx client directly, but beware that this will override any existing settings (e.g., base_url):
You can even set the httpx2 client directly, but beware that this will override any existing settings (e.g., base_url):

```python
import httpx
import httpx2
from my_test_api_client import Client

client = Client(
base_url="https://api.example.com",
)
# Note that base_url needs to be re-set, as would any shared cookies, headers, etc.
client.set_httpx_client(httpx.Client(base_url="https://api.example.com", proxies="http://localhost:8030"))
client.set_httpx2_client(httpx2.Client(base_url="https://api.example.com", proxy="http://localhost:8030"))
```

## Building / publishing this package
Expand Down
Loading