diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f534c..95d0385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # CHANGELOG -## Next Release +## v10.2.0 (2025-11-10) - Adds `UspsShipAccount` support to the create carrier method - Adds `tracker.retrieve_batch` function +- Adds `verify_carrier` address param ## v10.1.0 (2025-06-18) diff --git a/easypost/constant.py b/easypost/constant.py index bd8cec8..372e62e 100644 --- a/easypost/constant.py +++ b/easypost/constant.py @@ -1,6 +1,6 @@ # flake8: noqa # Library version -VERSION = "10.1.0" +VERSION = "10.2.0" VERSION_INFO = [str(number) for number in VERSION.split(".")] # Client defaults diff --git a/easypost/services/address_service.py b/easypost/services/address_service.py index 42db7fe..bea69ba 100644 --- a/easypost/services/address_service.py +++ b/easypost/services/address_service.py @@ -21,6 +21,7 @@ def create( self, verify: Optional[bool] = None, verify_strict: Optional[bool] = None, + verify_carrier: Optional[str] = None, **params, ) -> Address: """Create an Address.""" @@ -31,6 +32,8 @@ def create( wrapped_params["verify"] = verify if verify_strict: wrapped_params["verify_strict"] = verify_strict + if verify_carrier: + wrapped_params["verify_carrier"] = verify_carrier response = Requestor(self._client).request(method=RequestMethod.POST, url=url, params=wrapped_params) @@ -48,10 +51,13 @@ def retrieve(self, id) -> Address: """Retrieve an Address.""" return self._retrieve_resource(self._model_class, id) - def create_and_verify(self, **params) -> Address: + def create_and_verify(self, verify_carrier: Optional[str] = None, **params) -> Address: """Create and verify an Address in one call.""" url = f"{self._class_url('address')}/create_and_verify" - wrapped_params = {self._snakecase_name(self._model_class): params} + wrapped_params = {self._snakecase_name(self._model_class): params} # type: dict[str, Any] + + if verify_carrier: + wrapped_params["verify_carrier"] = verify_carrier response = Requestor(self._client).request(method=RequestMethod.POST, url=url, params=wrapped_params) diff --git a/setup.py b/setup.py index 9dc284c..d414755 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup( name="easypost", - version="10.1.0", + version="10.2.0", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", diff --git a/tests/cassettes/test_address_create_and_verify_carrier.yaml b/tests/cassettes/test_address_create_and_verify_carrier.yaml new file mode 100644 index 0000000..065e80d --- /dev/null +++ b/tests/cassettes/test_address_create_and_verify_carrier.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + body: '{"address": {"company": "EasyPost", "street1": "000 unknown street", "city": + "Not A City", "state": "ZZ", "zip": "00001", "country": "US", "email": "test@example.com", + "phone": "5555555555"}, "verify_carrier": "UPS"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '216' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: POST + uri: https://api.easypost.com/v2/addresses/create_and_verify + response: + body: + string: '{"address": {"id": "adr_b532f4f1bc0311f0b86eac1f6bc539aa", "object": + "Address", "created_at": "2025-11-07T18:00:55Z", "updated_at": "2025-11-07T18:00:55Z", + "name": null, "company": "EASYPOST", "street1": "000 UNKNOWN STREET", "street2": + "", "city": "NOT A CITY", "state": "ZZ", "zip": "00001", "country": "US", + "phone": "", "email": "", "mode": "test", "carrier_facility": + null, "residential": null, "federal_tax_id": null, "state_tax_id": null, "verifications": + {"zip4": {"success": true, "errors": [{"code": "E.ADDRESS.NOT_FOUND", "field": + "address", "message": "Address not found", "suggestion": null}], "details": + null}, "delivery": {"success": true, "errors": [{"code": "E.ADDRESS.NOT_FOUND", + "field": "address", "message": "Address not found", "suggestion": null}], + "details": {"latitude": null, "longitude": null, "time_zone": null}}, "verify_carrier": + "ups"}}}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '820' + content-type: + - application/json; charset=utf-8 + expires: + - '0' + location: + - /api/v2/addresses/adr_b532f4f1bc0311f0b86eac1f6bc539aa + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - bcd43723690e33d7e786a7bd02a1cbd9 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb42nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb3nuq c0061e0a2e + - extlb2nuq cbbd141214 + x-runtime: + - '0.492859' + x-version-label: + - easypost-202511071655-85836cab32-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_address_create_verify_carrier.yaml b/tests/cassettes/test_address_create_verify_carrier.yaml new file mode 100644 index 0000000..61aa952 --- /dev/null +++ b/tests/cassettes/test_address_create_verify_carrier.yaml @@ -0,0 +1,82 @@ +interactions: +- request: + body: '{"address": {"company": "EasyPost", "street1": "000 unknown street", "city": + "Not A City", "state": "ZZ", "zip": "00001", "country": "US", "email": "test@example.com", + "phone": "5555555555"}, "verify": true, "verify_carrier": "UPS"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '232' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: POST + uri: https://api.easypost.com/v2/addresses + response: + body: + string: '{"id": "adr_6bbec015bb5011f082233cecef1b359e", "object": "Address", + "created_at": "2025-11-06T20:37:32Z", "updated_at": "2025-11-06T20:37:32Z", + "name": null, "company": "EASYPOST", "street1": "000 UNKNOWN STREET", "street2": + "", "city": "NOT A CITY", "state": "ZZ", "zip": "00001", "country": "US", + "phone": "", "email": "", "mode": "test", "carrier_facility": + null, "residential": null, "federal_tax_id": null, "state_tax_id": null, "verifications": + {"zip4": {"success": true, "errors": [{"code": "E.ADDRESS.NOT_FOUND", "field": + "address", "message": "Address not found", "suggestion": null}], "details": + null}, "delivery": {"success": true, "errors": [{"code": "E.ADDRESS.NOT_FOUND", + "field": "address", "message": "Address not found", "suggestion": null}], + "details": {"latitude": null, "longitude": null, "time_zone": null}}, "verify_carrier": + "ups"}}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '808' + content-type: + - application/json; charset=utf-8 + expires: + - '0' + location: + - /api/v2/addresses/adr_6bbec015bb5011f082233cecef1b359e + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - bcd43725690d070be2b85def014ed69a + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb66nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb4nuq c0061e0a2e + - extlb2nuq cbbd141214 + x-runtime: + - '0.837269' + x-version-label: + - easypost-202511061930-711db047b0-master + x-xss-protection: + - 1; mode=block + status: + code: 201 + message: Created +version: 1 diff --git a/tests/test_address.py b/tests/test_address.py index b05f640..346a930 100644 --- a/tests/test_address.py +++ b/tests/test_address.py @@ -155,3 +155,28 @@ def test_address_verify_invalid_address(test_client): test_client.address.verify(address.id) assert str(error.value) == "Unable to verify address." + + +@pytest.mark.vcr() +def test_address_create_verify_carrier(incorrect_address, test_client): + """Test creating an address with the `verify_carrier` param.""" + incorrect_address["verify"] = True + incorrect_address["verify_carrier"] = "UPS" + address = test_client.address.create(**incorrect_address) + + assert isinstance(address, Address) + + assert address.verifications.delivery.errors[0].message == "Address not found" + assert address.verifications.zip4.errors[0].message == "Address not found" + + +@pytest.mark.vcr() +def test_address_create_and_verify_carrier(incorrect_address, test_client): + """Test creating and verifying an address with the `verify_carrier` param.""" + incorrect_address["verify_carrier"] = "UPS" + address = test_client.address.create_and_verify(**incorrect_address) + + assert isinstance(address, Address) + + assert address.verifications.delivery.errors[0].message == "Address not found" + assert address.verifications.zip4.errors[0].message == "Address not found"