Skip to content

Commit ae0db68

Browse files
authored
feat: add a generic API request interface (#384)
# Description Adds a generic API request interface to make arbitrary API requests. <!-- Please provide a general summary of your PR changes and link any related issues or other pull requests. --> # Testing <!-- Please provide details on how you tested this code. See below. - All pull requests must be tested (unit tests where possible with accompanying cassettes, or provide a screenshot of end-to-end testing when unit tests are not possible) - New features must get a new unit test - Bug fixes/refactors must re-record existing cassettes --> # Pull Request Type Please select the option(s) that are relevant to this PR. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Improvement (fixing a typo, updating readme, renaming a variable name, etc)
1 parent 95b9229 commit ae0db68

File tree

6 files changed

+112
-7
lines changed

6 files changed

+112
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## v10.6.0 (2026-02-25)
4+
5+
- Adds generic `make_api_call` function
6+
37
## v10.5.1 (2026-02-24)
48

59
- Corrects the return type of `carrier_account.all()` (closes #386)

easypost/constant.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# flake8: noqa
22
# Library version
3-
VERSION = "10.5.1"
3+
VERSION = "10.6.0"
44
VERSION_INFO = [str(number) for number in VERSION.split(".")]
55

66
# Client defaults

easypost/easypost_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
from typing import Any
2+
13
from easypost.constant import (
24
API_BASE,
35
API_VERSION,
46
INVALID_REQUESTS_VERSION_ERROR,
57
SUPPORT_EMAIL,
68
TIMEOUT,
79
)
10+
from easypost.easypost_object import convert_to_easypost_object
811
from easypost.hooks import RequestHook, ResponseHook
12+
from easypost.requestor import RequestMethod, Requestor
913
from easypost.services import (
1014
AddressService,
1115
ApiKeyService,
@@ -138,3 +142,14 @@ def subscribe_to_response_hook(self, function):
138142
def unsubscribe_from_response_hook(self, function):
139143
"""Unsubscribe functions from running when a response occurs."""
140144
self._response_hook -= function
145+
146+
def make_api_call(self, method: RequestMethod, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
147+
"""Make an API call to the EasyPost API.
148+
149+
This public, generic interface is useful for making arbitrary API calls to the EasyPost API that
150+
are not yet supported by the client library's services. When possible, the service for your use case
151+
should be used instead as it provides a more convenient and higher-level interface depending on the endpoint.
152+
"""
153+
response = Requestor(self).request(method=method, url=endpoint, params=params)
154+
155+
return convert_to_easypost_object(response=response)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[project]
66
name = "easypost"
77
description = "EasyPost Shipping API Client Library for Python"
8-
version = "10.5.1"
8+
version = "10.6.0"
99
readme = "README.md"
1010
requires-python = ">=3.9"
1111
license = { file = "LICENSE" }

tests/cassettes/test_client_make_api_call.yaml

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_easypost_client.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
from easypost.easypost_client import EasyPostClient
88
from easypost.errors import TimeoutError
9+
from easypost.requestor import RequestMethod
910

1011

11-
def test_api_key():
12+
def test_easypost_client_api_key():
1213
"""Tests setting and getting API keys from different client objects."""
1314
client1 = EasyPostClient(api_key="123")
1415
assert client1.api_key == "123"
@@ -17,23 +18,23 @@ def test_api_key():
1718
assert client2.api_key == "456"
1819

1920

20-
def test_no_api_key():
21+
def test_easypost_client_no_api_key():
2122
"""Tests that we raise an error when no API key is passed to the client."""
2223
with pytest.raises(TypeError) as error:
2324
EasyPostClient()
2425

2526
assert "missing 1 required positional argument: 'api_key'" in str(error.value)
2627

2728

28-
def test_invalid_client_property():
29+
def test_easypost_client_invalid_client_property():
2930
"""Tests that we throw an error when attempting to use an invalid property of a client."""
3031
with pytest.raises(AttributeError) as error:
3132
EasyPostClient("123").invalid_property()
3233

3334
assert str(error.value) == "'EasyPostClient' object has no attribute 'invalid_property'"
3435

3536

36-
def test_api_base():
37+
def test_easypost_client_api_base():
3738
"""Tests that we can override the API base of the client object."""
3839
client1 = EasyPostClient(api_key="123")
3940
assert client1.api_base == "https://api.easypost.com/v2"
@@ -43,7 +44,7 @@ def test_api_base():
4344

4445

4546
@patch("requests.Session")
46-
def test_client_timeout(mock_session, basic_shipment):
47+
def test_easypost_client_timeout(mock_session, basic_shipment):
4748
"""Tests that the timeout gets used properly in requests when set."""
4849
mock_session().request.side_effect = requests.exceptions.Timeout()
4950
client = EasyPostClient(api_key=os.getenv("EASYPOST_TEST_API_KEY"), timeout=0.1)
@@ -53,3 +54,14 @@ def test_client_timeout(mock_session, basic_shipment):
5354
assert False
5455
except TimeoutError as error:
5556
assert error.message == "Request timed out."
57+
58+
59+
@pytest.mark.vcr()
60+
def test_client_make_api_call():
61+
"""Tests that we can make an API call using the generic make_api_call method."""
62+
client = EasyPostClient(api_key=os.getenv("EASYPOST_TEST_API_KEY"))
63+
64+
response = client.make_api_call(method=RequestMethod.GET, endpoint="/addresses", params={"page_size": 1})
65+
66+
assert len(response["addresses"]) == 1
67+
assert response["addresses"][0]["object"] == "Address"

0 commit comments

Comments
 (0)