Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
Loading