Conversation
| writer.addImport("smithy_core.aio.utils", "async_list"); | ||
|
|
||
| writer.write(""" | ||
| def _assert_modeled_value(actual: object, expected: object) -> None: |
There was a problem hiding this comment.
I'm not a fan of this. When you do a plain assert with two dataclasses, pytest will give you a nice result showing you the diff if they're not equal. This will fail at the first non-equal value. Also, since it only recurses on dataclasses it'll never find nan in a list or map.
In the spirit of moving things out of Java, it might be best to have a test-utils package that implements an assert that builds up an error. It could go in test dependencies so normal installs don't catch it.
There was a problem hiding this comment.
Yea, this is more complicated that I thought. I'm gonna back track from this here so the scope is on JSON-RPC and I don't add complexity to this PR.
I played around with this approach for replacing NaN's with a custom object that won't fail on the ==. But I'm not a fan of having to rebuild the object just for this.
class _ModeledNaN:
def __repr__(self) -> str:
return "float('nan')"
class _NormalizedDocument:
def __init__(self, value: object) -> None:
self.value = value
def __eq__(self, other: object) -> bool:
return isinstance(other, _NormalizedDocument) and self.value == other.value
def __repr__(self) -> str:
return f"Document(value={self.value!r})"
_MODELED_NAN = _ModeledNaN()
def _normalize_modeled_value(value: Any) -> Any:
if isinstance(value, float) and isnan(value):
return _MODELED_NAN
if isinstance(value, Document):
return _NormalizedDocument(_normalize_modeled_value(value.as_value()))
if is_dataclass(value) and not isinstance(value, type):
return cast(Any, type(value))(
**{
field.name: _normalize_modeled_value(getattr(value, field.name))
for field in fields(value)
if field.init
},
)
if isinstance(value, list):
return [_normalize_modeled_value(member) for member in cast(list[Any], value)]
if isinstance(value, tuple):
return tuple(
_normalize_modeled_value(member) for member in cast(tuple[Any, ...], value)
)
if isinstance(value, dict):
return {
key: _normalize_modeled_value(member)
for key, member in cast(dict[Any, Any], value).items()
}
return value
def _assert_modeled_value(actual: Any, expected: Any) -> None:
assert _normalize_modeled_value(actual) == _normalize_modeled_value(expected)I'm gonna revert these changes and ignore the nan tests again. I'll create an issue for us to track the work for getting a proper solution.
There was a problem hiding this comment.
Yeah it's a bit complex. What if you just to a recursive equalty check if it's a known nan test? I'm okay with the rest result being less good for those tests specifically. I think it should be pretty easy to traverse the document during codegen to see if there's a nan value.
...core/src/main/java/software/amazon/smithy/python/aws/codegen/AwsJson10ProtocolGenerator.java
Outdated
Show resolved
Hide resolved
...core/src/main/java/software/amazon/smithy/python/aws/codegen/AwsJson11ProtocolGenerator.java
Outdated
Show resolved
Hide resolved
| from smithy_core.traits import DynamicTrait, Trait | ||
|
|
||
|
|
||
| def _parse_http_protocol_values( |
There was a problem hiding this comment.
This could really use a comment explaining what's going on.
There was a problem hiding this comment.
Added in 3d2f6c5
Let me know if you find it helpful. If not, I'm happy to update it
| return ShapeID.from_parts(name=code, namespace=default_namespace) | ||
|
|
||
|
|
||
| def parse_header_error_code(code: str, default_namespace: str | None) -> ShapeID | None: |
There was a problem hiding this comment.
Why does this function exist? The function above should handle it everything. If it's not handling some edge case, it should be updated.
There was a problem hiding this comment.
Thanks for asking about this. Turns out I was misinterpreting some failed protocol tests that turned out to be false negatives. I removed parse_header_error_code and refactored some other pieces to make things simpler.
| has_host_prefix = bool(previous.host) and previous.host != "." | ||
| host = uri.host | ||
| if has_host_prefix and uri.host and previous.host.endswith("."): | ||
| host = f"{previous.host}{uri.host}" | ||
| elif has_host_prefix: | ||
| host = previous.host |
There was a problem hiding this comment.
This was another misunderstanding on my part. I was trying to compensate for something we don't support yet. I added the related protocol tests to the skip list in 05c957e. We ignore them for REST-JSON.
| case int(): | ||
| return Decimal(event.value) | ||
| case float(): | ||
| return Decimal.from_float(event.value) | ||
| case "Infinity" | "-Infinity" | "NaN": | ||
| return Decimal(event.value) |
There was a problem hiding this comment.
If the outcome is the same, they should be combined
Reviewer Notes:
SummaryNon-event stream operations are working for service clients that use the JSON-RPC protocol (see SQS example in the description). However, there is some additional work to do to handle event streams. I have a local fixes that address both issues mentioned above which are seen when testing with kinesis and logs clients. I'm thinking I should scope this PR to just add support for non-event stream JSON-RPC support and follow up with a proper event stream PR that includes the fixes and event stream protocol tests. Open to feedback |
|
Update: I added a fallback in modeled error resolution for cases where the wire error ShapeID uses a different namespace than the modeled error registered on the operation. We still prefer an exact Protocol tests are now passing and we don't need to wait for smithy-lang/smithy#2990 |
| // TODO: support client error-correction behavior when the server | ||
| // omits required values in modeled error responses. | ||
| "AwsJson10ClientErrorCorrectsWhenServerFailsToSerializeRequiredValues", | ||
| "AwsJson10ClientErrorCorrectsWithDefaultValuesWhenServerFailsToSerializeRequiredValues"); |
There was a problem hiding this comment.
Didn't you say you fixed this?
|
|
||
| return request | ||
|
|
||
| def _resolve_error_id( |
There was a problem hiding this comment.
This isn't the right place for this as it's a quirk of how AWS SDKs have historically been generated. There is already a method that handles such quirks, and before you'd created a new one right next to it. Why not just update that?
| return event.value | ||
| case int() | float(): | ||
| return Decimal.from_float(event.value) | ||
| case "Infinity" | "-Infinity" | "NaN": |
There was a problem hiding this comment.
github is being weird and won't let me do a code suggestion, but this also needs to be moved up the same way as was done for float
| ) -> bool: | ||
| return 200 <= response.status < 300 | ||
|
|
||
| def _resolve_host_prefix( |
There was a problem hiding this comment.
You either gotta be running the tests for this or you gotta not have an implementation
| return content_length == 0 | ||
| return False | ||
|
|
||
| async def _create_error( |
There was a problem hiding this comment.
You should mention what's different here vs the default
Overview
This PR adds
awsJson1_0/awsJson1_1support: new protocol generators and protocol-test projections, plussmithy-aws-coreruntime client protocols with awsJson-specific error identification/discriminator parsing, host-prefix handling, and event-stream support. It also includes supporting infrastructure changes: improved generated SigV4 test config, shared HTTP endpoint host-prefix merging, and smithy-json non-finite numeric serde (NaN/Infinity) round-trip support.Note
This PR does not implement event streaming support for JSON-RPC. Of the 149 AWS services that use this protocol, only 2 of them use event streaming. This will be implemented as a follow up along with proper event streaming protocol tests.
Testing
I was able to generate a client for Amazon SQS from its smithy service model and make successfully calls:
SQS Test Script
Output:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.