Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/http-client-python"
---

Always populate the operation `error_map` with the standard azure-core error types (401 → `ClientAuthenticationError`, 404 → `ResourceNotFoundError`, 409 → `ResourceExistsError`, 304 → `ResourceNotModifiedError`), even when a customized error model covers those status codes. Previously, a standard status code covered by a customized ranged or default error model fell back to a generic `HttpResponseError`; it now raises its dedicated error type via `map_error`. The customized error body continues to be deserialized and attached to the `HttpResponseError` raised for other (non-standard) status codes.
Original file line number Diff line number Diff line change
Expand Up @@ -1088,25 +1088,12 @@ def handle_error_response( # pylint: disable=too-many-statements, too-many-bran
" )",
]
)
# add build-in error type
# TODO: we should decide whether need to this wrapper for customized error type
status_code_error_map = {
401: "ClientAuthenticationError",
404: "ResourceNotFoundError",
409: "ResourceExistsError",
304: "ResourceNotModifiedError",
}
if status_code in status_code_error_map:
retval.append(
" raise {}(response=response{}{})".format(
status_code_error_map[cast(int, status_code)],
error_model,
(", error_format=ARMErrorFormat" if self.code_model.options["azure-arm"] else ""),
)
)
condition = "if"
else:
condition = "elif"
# The dedicated azure-core error type for a standard status
# code is raised by ``map_error`` via the error map, so here
# we only deserialize the customized error body (used by the
# generic ``HttpResponseError`` fallback for non-standard
# status codes within the response).
condition = "elif"
# ranged status code only exist in typespec and will not have multiple status codes
else:
retval.append(
Expand Down Expand Up @@ -1238,36 +1225,17 @@ def handle_response(self, builder: OperationType) -> list[str]:
retval.append("return 200 <= response.status_code <= 299")
return retval

def _need_specific_error_map(self, code: int, builder: OperationType) -> bool:
for non_default_error in builder.non_default_errors:
# single status code
if code in non_default_error.status_codes:
return False
# ranged status code
if (
isinstance(non_default_error.status_codes[0], list)
and non_default_error.status_codes[0][0] <= code <= non_default_error.status_codes[0][1]
):
return False
return True

def error_map(self, builder: OperationType) -> list[str]:
retval = ["error_map: MutableMapping = {"]
if builder.non_default_errors and self.code_model.options["models-mode"]:
# TODO: we should decide whether to add the build-in error map when there is a customized default error type
if self._need_specific_error_map(401, builder):
retval.append(" 401: ClientAuthenticationError,")
if self._need_specific_error_map(404, builder):
retval.append(" 404: ResourceNotFoundError,")
if self._need_specific_error_map(409, builder):
retval.append(" 409: ResourceExistsError,")
if self._need_specific_error_map(304, builder):
retval.append(" 304: ResourceNotModifiedError,")
else:
retval.append(
" 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, "
"304: ResourceNotModifiedError"
)
# Always map the standard status codes to their dedicated azure-core error
Comment thread
l0lawrence marked this conversation as resolved.
# type so ``map_error`` raises the correct semantic error, even when a
# customized error model (single, ranged, or default) covers them. The
# customized error body is still deserialized in ``handle_error_response``
# for the generic ``HttpResponseError`` fallback.
retval.append(
" 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, "
"304: ResourceNotModifiedError"
)
retval.append("}")
if builder.has_etag:
retval.extend(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest
import pytest_asyncio
from response.statuscoderange.aio import StatusCodeRangeClient
from response.statuscoderange.models import ErrorInRange, NotFoundError
from response.statuscoderange.models import ErrorInRange


@pytest_asyncio.fixture
Expand All @@ -29,11 +29,9 @@ async def test_error_response_status_code_in_range(client: StatusCodeRangeClient

@pytest.mark.asyncio
async def test_error_response_status_code_404(client: StatusCodeRangeClient, core_library):
# 404 maps to the dedicated azure-core ``ResourceNotFoundError`` via ``map_error``,
# which raises before the customized error body is deserialized, so no model is attached.
with pytest.raises(core_library.exceptions.ResourceNotFoundError) as exc_info:
await client.error_response_status_code404()

error = exc_info.value.model
assert isinstance(error, NotFoundError)
assert error.code == "not-found"
assert error.resource_id == "resource1"
assert exc_info.value.response.status_code == 404
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# --------------------------------------------------------------------------
import pytest
from response.statuscoderange import StatusCodeRangeClient
from response.statuscoderange.models import ErrorInRange, NotFoundError
from response.statuscoderange.models import ErrorInRange


@pytest.fixture
Expand All @@ -26,11 +26,9 @@ def test_error_response_status_code_in_range(client: StatusCodeRangeClient, core


def test_error_response_status_code_404(client: StatusCodeRangeClient, core_library):
# 404 maps to the dedicated azure-core ``ResourceNotFoundError`` via ``map_error``,
# which raises before the customized error body is deserialized, so no model is attached.
with pytest.raises(core_library.exceptions.ResourceNotFoundError) as exc_info:
client.error_response_status_code404()

error = exc_info.value.model
assert isinstance(error, NotFoundError)
assert error.code == "not-found"
assert error.resource_id == "resource1"
assert exc_info.value.response.status_code == 404
Loading