Skip to content

Commit 0d8e80f

Browse files
committed
checkpoint 3
1 parent 5d82e4a commit 0d8e80f

File tree

9 files changed

+210
-795
lines changed

9 files changed

+210
-795
lines changed

codegen/core/src/main/java/software/amazon/smithy/python/codegen/ClientGenerator.java

Lines changed: 54 additions & 764 deletions
Large diffs are not rendered by default.

codegen/core/src/main/java/software/amazon/smithy/python/codegen/DirectedPythonClientCodegen.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ public void customizeBeforeShapeGeneration(CustomizeDirective<GenerationContext,
110110

111111
new ConfigGenerator(directive.settings(), directive.context()).run();
112112
var serviceIndex = ServiceIndex.of(directive.model());
113-
if (directive.context().applicationProtocol().isHttpProtocol()
114-
&& !serviceIndex.getAuthSchemes(directive.service()).isEmpty()) {
115-
new HttpAuthGenerator(directive.context(), directive.settings()).run();
113+
if (directive.context().applicationProtocol().isHttpProtocol()) {
114+
if (!serviceIndex.getAuthSchemes(directive.service()).isEmpty()) {
115+
new HttpAuthGenerator(directive.context(), directive.settings()).run();
116+
}
116117
new EndpointsGenerator(directive.context(), directive.settings()).run();
117118
}
118119
}

codegen/core/src/main/java/software/amazon/smithy/python/codegen/PythonSymbolProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public Symbol operationShape(OperationShape shape) {
277277
// Operation names are escaped like members because ultimately they're
278278
// properties on an object too.
279279
var methodName = escaper.escapeMemberName(CaseUtils.toSnakeCase(shape.getId().getName(service)));
280-
var methodSymbol = createGeneratedSymbolBuilder(shape, methodName, "client").build();
280+
var methodSymbol = createGeneratedSymbolBuilder(shape, methodName, "client", false).build();
281281

282282
// We add a symbol for the method in the client as a property, whereas the actual
283283
// operation symbol points to the generated type for it

codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/OperationGenerator.java

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,18 @@
44
*/
55
package software.amazon.smithy.python.codegen.generators;
66

7-
import software.amazon.smithy.codegen.core.Symbol;
7+
import java.util.List;
8+
import java.util.logging.Logger;
89
import software.amazon.smithy.codegen.core.SymbolProvider;
910
import software.amazon.smithy.model.Model;
1011
import software.amazon.smithy.model.knowledge.ServiceIndex;
1112
import software.amazon.smithy.model.shapes.OperationShape;
12-
import software.amazon.smithy.model.shapes.ServiceShape;
1313
import software.amazon.smithy.model.shapes.ShapeId;
14-
import software.amazon.smithy.python.codegen.CodegenUtils;
1514
import software.amazon.smithy.python.codegen.GenerationContext;
1615
import software.amazon.smithy.python.codegen.SymbolProperties;
1716
import software.amazon.smithy.python.codegen.writer.PythonWriter;
1817
import software.amazon.smithy.utils.SmithyInternalApi;
1918

20-
import java.util.List;
21-
import java.util.logging.Logger;
22-
2319
@SmithyInternalApi
2420
public final class OperationGenerator implements Runnable {
2521
private static final Logger LOGGER = Logger.getLogger(OperationGenerator.class.getName());
@@ -30,7 +26,6 @@ public final class OperationGenerator implements Runnable {
3026
private final SymbolProvider symbolProvider;
3127
private final Model model;
3228

33-
3429
public OperationGenerator(GenerationContext context, PythonWriter writer, OperationShape shape) {
3530
this.context = context;
3631
this.writer = writer;
@@ -52,7 +47,7 @@ public void run() {
5247
writer.write("""
5348
@dataclass(kw_only=True, frozen=True)
5449
class $1L(APIOperation["$2T", "$3T"]):
55-
input = $2T
50+
input = $2T
5651
output = $3T
5752
schema = $4T
5853
input_schema = $5T
@@ -72,8 +67,8 @@ class $1L(APIOperation["$2T", "$3T"]):
7267
outSymbol.expectProperty(SymbolProperties.SCHEMA),
7368
writer.consumer(this::writeErrorTypeRegistry),
7469
writer.consumer(this::writeAuthSchemes)
75-
// TODO: Docs? Maybe not necessary on the operation type itself
76-
// TODO: Singleton?
70+
// TODO: Docs? Maybe not necessary on the operation type itself
71+
// TODO: Singleton?
7772
);
7873
}
7974

@@ -89,14 +84,16 @@ private void writeErrorTypeRegistry(PythonWriter writer) {
8984
}
9085

9186
private void writeAuthSchemes(PythonWriter writer) {
92-
var authSchemes = ServiceIndex.of(model).getEffectiveAuthSchemes(context.settings().service(), shape.getId(),
93-
ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE);
87+
var authSchemes = ServiceIndex.of(model)
88+
.getEffectiveAuthSchemes(context.settings().service(),
89+
shape.getId(),
90+
ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE);
9491

9592
if (!authSchemes.isEmpty()) {
9693
writer.addImport("smithy_core.shapes", "ShapeID");
9794
}
9895

99-
for(var authSchemeId : authSchemes.keySet()) {
96+
for (var authSchemeId : authSchemes.keySet()) {
10097
writer.write("ShapeID($S)", authSchemeId);
10198
}
10299

codegen/core/src/main/java/software/amazon/smithy/python/codegen/writer/PythonWriter.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
import software.amazon.smithy.model.node.NumberNode;
2222
import software.amazon.smithy.model.node.ObjectNode;
2323
import software.amazon.smithy.model.node.StringNode;
24-
import software.amazon.smithy.model.shapes.Shape;
2524
import software.amazon.smithy.python.codegen.CodegenUtils;
2625
import software.amazon.smithy.python.codegen.PythonSettings;
27-
import software.amazon.smithy.python.codegen.SymbolProperties;
2826
import software.amazon.smithy.utils.SmithyUnstableApi;
2927
import software.amazon.smithy.utils.StringUtils;
3028

@@ -305,9 +303,7 @@ public String apply(Object type, String indent) {
305303
Symbol typeSymbol = (Symbol) type;
306304
// Check if the symbol is an operation - we shouldn't add imports for operations, since
307305
// they are methods of the service object and *can't* be imported
308-
if (!isOperationSymbol(typeSymbol)) {
309-
addUseImports(typeSymbol);
310-
}
306+
addUseImports(typeSymbol);
311307
return typeSymbol.getName();
312308
} else if (type instanceof SymbolReference) {
313309
SymbolReference typeSymbol = (SymbolReference) type;
@@ -320,10 +316,6 @@ public String apply(Object type, String indent) {
320316
}
321317
}
322318

323-
private Boolean isOperationSymbol(Symbol typeSymbol) {
324-
return typeSymbol.getProperty(SymbolProperties.SHAPE).map(Shape::isOperationShape).orElse(false);
325-
}
326-
327319
private final class PythonNodeFormatter implements BiFunction<Object, String, String> {
328320
private final PythonWriter writer;
329321

packages/smithy-core/src/smithy_core/aio/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ async def _handle_attempt[I: SerializeableShape, O: DeserializeableShape](
468468
operation=call.operation,
469469
request=response_context.transport_request,
470470
response=response_context.transport_response,
471-
error_registry="foo",
471+
error_registry=call.operation.error_registry,
472472
context=response_context.properties,
473473
)
474474

packages/smithy-core/src/smithy_core/aio/interfaces/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0
33
from collections.abc import AsyncIterable
4-
from typing import Protocol, runtime_checkable, TYPE_CHECKING, Any, Callable
4+
from typing import Protocol, runtime_checkable, TYPE_CHECKING, Callable
55

66
from ...exceptions import UnsupportedStreamException
77
from ...interfaces import URI, Endpoint, TypedProperties
88
from ...interfaces import StreamingBlob as SyncStreamingBlob
99

1010
from .eventstream import EventPublisher, EventReceiver
11-
11+
from ...type_registry import TypeRegistry
1212

1313
if TYPE_CHECKING:
1414
from ...schemas import APIOperation
@@ -129,7 +129,7 @@ async def deserialize_response[
129129
operation: "APIOperation[OperationInput, OperationOutput]",
130130
request: I,
131131
response: O,
132-
error_registry: Any, # TODO: add error registry
132+
error_registry: TypeRegistry,
133133
context: TypedProperties,
134134
) -> OperationOutput:
135135
"""Deserializes the output from the tranport response or throws an exception.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import os
2+
from inspect import iscoroutinefunction
3+
from io import BytesIO
4+
5+
from smithy_core.aio.interfaces import ClientProtocol
6+
from smithy_core.codecs import Codec
7+
from smithy_core.deserializers import DeserializeableShape
8+
from smithy_core.interfaces import Endpoint, TypedProperties, URI
9+
from smithy_core.schemas import APIOperation
10+
from smithy_core.serializers import SerializeableShape
11+
from smithy_core.shapes import ShapeID
12+
from smithy_core.traits import HTTPTrait, EndpointTrait, RestJson1Trait
13+
from smithy_core.type_registry import TypeRegistry
14+
from smithy_http.aio.interfaces import HTTPRequest, HTTPResponse
15+
from smithy_http.deserializers import HTTPResponseDeserializer
16+
from smithy_http.serializers import HTTPRequestSerializer
17+
from smithy_json import JSONCodec
18+
19+
20+
class HttpClientProtocol(ClientProtocol[HTTPRequest, HTTPResponse]):
21+
def set_service_endpoint(
22+
self,
23+
*,
24+
request: HTTPRequest,
25+
endpoint: Endpoint,
26+
) -> HTTPRequest:
27+
"""Update the endpoint of a transport request.
28+
29+
:param request: The request whose endpoint should be updated.
30+
:param endpoint: The endpoint to set on the request.
31+
"""
32+
uri = endpoint.uri
33+
uri_builder = request.destination
34+
35+
if uri.scheme:
36+
uri_builder.scheme = uri.scheme
37+
if uri.host:
38+
uri_builder.host = uri.host
39+
if uri.port and uri.port > -1:
40+
uri_builder.port = uri.port
41+
if uri.path:
42+
# TODO: verify, uri helper?
43+
uri_builder.path = os.path.join(uri.path, uri_builder.path or "")
44+
# TODO: merge headers from the endpoint properties bag
45+
return request
46+
47+
48+
class HttpBindingClientProtocol(HttpClientProtocol):
49+
@property
50+
def codec(self) -> Codec:
51+
"""The codec used for the serde of input and output shapes."""
52+
...
53+
54+
@property
55+
def content_type(self) -> str:
56+
"""The media type of the http payload."""
57+
...
58+
59+
def serialize_request[
60+
OperationInput: "SerializeableShape",
61+
OperationOutput: "DeserializeableShape",
62+
](
63+
self,
64+
*,
65+
operation: APIOperation[OperationInput, OperationOutput],
66+
input: OperationInput,
67+
endpoint: URI,
68+
context: TypedProperties,
69+
) -> HTTPRequest:
70+
# TODO: request binding cache like done in SJ
71+
serializer = HTTPRequestSerializer(
72+
payload_codec=self.codec,
73+
http_trait=operation.schema.expect_trait(HTTPTrait), # TODO
74+
endpoint_trait=operation.schema.get_trait(EndpointTrait),
75+
)
76+
77+
input.serialize(serializer=serializer)
78+
request = serializer.result
79+
80+
if request is None:
81+
raise ValueError("Request is None") # TODO
82+
83+
request.fields["content-type"].add(self.content_type)
84+
return request
85+
86+
async def deserialize_response[
87+
OperationInput: "SerializeableShape",
88+
OperationOutput: "DeserializeableShape",
89+
](
90+
self,
91+
*,
92+
operation: APIOperation[OperationInput, OperationOutput],
93+
request: HTTPRequest,
94+
response: HTTPResponse,
95+
error_registry: TypeRegistry,
96+
context: TypedProperties,
97+
) -> OperationOutput:
98+
if not (200 <= response.status <= 299): # TODO: extract to utility
99+
# TODO: implement error serde from type registry
100+
raise NotImplementedError
101+
102+
body = response.body
103+
# TODO: extract to utility, seems common
104+
if (read := getattr(body, "read", None)) is not None and iscoroutinefunction(
105+
read
106+
):
107+
body = BytesIO(await read())
108+
109+
# TODO: response binding cache like done in SJ
110+
deserializer = HTTPResponseDeserializer(
111+
payload_codec=self.codec,
112+
http_trait=operation.schema.expect_trait(HTTPTrait),
113+
response=response,
114+
body=body, # type: ignore
115+
)
116+
117+
return operation.output.deserialize(deserializer)
118+
119+
120+
class RestJsonClientProtocol(HttpBindingClientProtocol):
121+
_id: ShapeID = RestJson1Trait.id
122+
_codec: JSONCodec = JSONCodec()
123+
_contentType: str = "application/json"
124+
125+
@property
126+
def id(self) -> ShapeID:
127+
return self._id
128+
129+
@property
130+
def codec(self) -> Codec:
131+
return self._codec
132+
133+
@property
134+
def content_type(self) -> str:
135+
return self._contentType

packages/smithy-core/src/smithy_core/type_registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
# A registry for on-demand deserialization of types by using a mapping of shape IDs to their deserializers.
12-
# TODO: protocol
12+
# TODO: protocol? Also, move into documents.py?
1313
class TypeRegistry:
1414
def __init__(
1515
self,

0 commit comments

Comments
 (0)