-
Notifications
You must be signed in to change notification settings - Fork 197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Codegenerate StructureShape, BlobShape, application, server and Python runtime #1403
Changes from 15 commits
4367c00
8cca266
49a7656
cc46039
5e68c85
8afe768
95d128f
95d9db2
bc6afd7
e6d2d53
52881c1
1585aa7
7cf4615
8b36d85
3082c83
8e90b1a
45ed6b6
28c4a06
4eda17e
52b7eba
1c9c969
543d207
134cf5d
c972b87
f65cbf1
f8925e3
1313869
8ca146c
def14e2
41cf120
cf66d41
f6037b4
b08c324
3f6b40b
0c86e15
d6f7e21
7ccc719
db3d196
b31869d
fd90b59
2446e13
302d3d3
f18bb60
4f4c307
4a0ff86
b174639
f6afd03
8dab93e
f69c211
671e0aa
893c3cd
4cdfd67
d8a1a40
ef8b592
e4d2b4e
b314f1c
276519b
15e1b34
ec2ad28
8fe764e
4240344
50ba6cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.server.python.smithy | ||
|
||
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency | ||
import software.amazon.smithy.rust.codegen.rustlang.CratesIo | ||
import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig | ||
|
||
/** | ||
* Object used *exclusively* in the runtime of the Python server, for separation concerns. | ||
* Analogous to the companion object in [CargoDependency] and [ServerCargoDependency]; see its documentation for details. | ||
* For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. | ||
*/ | ||
object PythonServerCargoDependency { | ||
val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.16"), features = setOf("extension-module")) | ||
val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.16"), features = setOf("attributes", "tokio-runtime")) | ||
val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.0"), features = setOf("full")) | ||
val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) | ||
val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) | ||
val TowerHttp: CargoDependency = CargoDependency("tower-http", CratesIo("0.3"), features = setOf("trace")) | ||
val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14"), features = setOf("server", "http1", "http2", "tcp", "stream")) | ||
val NumCpus: CargoDependency = CargoDependency("num_cpus", CratesIo("1.13")) | ||
|
||
fun SmithyHttpServer(runtimeConfig: RuntimeConfig) = runtimeConfig.runtimeCrate("http-server") | ||
fun SmithyHttpServerPython(runtimeConfig: RuntimeConfig) = runtimeConfig.runtimeCrate("http-server-python") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
|
||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.server.python.smithy | ||
|
||
import software.amazon.smithy.build.PluginContext | ||
import software.amazon.smithy.model.shapes.ServiceShape | ||
import software.amazon.smithy.model.shapes.StringShape | ||
import software.amazon.smithy.model.shapes.StructureShape | ||
import software.amazon.smithy.model.traits.EnumTrait | ||
import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator | ||
import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerServiceGenerator | ||
import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator | ||
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor | ||
import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader | ||
import software.amazon.smithy.rust.codegen.smithy.CodegenContext | ||
import software.amazon.smithy.rust.codegen.smithy.CodegenMode | ||
import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules | ||
import software.amazon.smithy.rust.codegen.smithy.RustCrate | ||
import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig | ||
import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator | ||
import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator | ||
import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget | ||
import software.amazon.smithy.rust.codegen.smithy.generators.implBlock | ||
import software.amazon.smithy.rust.codegen.util.getTrait | ||
|
||
/** | ||
* Entrypoint for Python server-side code generation. This class will walk the in-memory model and | ||
* generate all the needed types by calling the accept() function on the available shapes. | ||
* | ||
* This class inherits from [ServerCodegenVisitor] since it uses most of the functionlities of the super class | ||
* and have to override the symbol provider with [PythonServerSymbolProvider]. | ||
*/ | ||
class PythonServerCodegenVisitor(context: PluginContext, private val codegenDecorator: RustCodegenDecorator) : | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ServerCodegenVisitor(context, codegenDecorator) { | ||
|
||
init { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is very similar, but it is needed to setup
and it is required to allow the inheritance to work and override the symbolProvider and everything else using it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could have a function that given a symbol provider, sets up the rest of the stuff. |
||
val symbolVisitorConfig = | ||
SymbolVisitorConfig( | ||
runtimeConfig = settings.runtimeConfig, | ||
codegenConfig = settings.codegenConfig, | ||
handleRequired = true | ||
) | ||
val baseModel = baselineTransform(context.model) | ||
val service = settings.getService(baseModel) | ||
val (protocol, generator) = | ||
ServerProtocolLoader( | ||
codegenDecorator.protocols( | ||
service.id, | ||
ServerProtocolLoader.DefaultProtocols | ||
) | ||
) | ||
.protocolFor(context.model, service) | ||
protocolGeneratorFactory = generator | ||
model = generator.transformModel(codegenDecorator.transformModel(service, baseModel)) | ||
val baseProvider = PythonCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) | ||
// Override symbolProvider. | ||
symbolProvider = | ||
codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) | ||
|
||
// Override `codegenContext` which carries the symbolProvider. | ||
codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, mode = CodegenMode.Server) | ||
|
||
// Override `rustCrate` which carries the symbolProvider. | ||
rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) | ||
// Override `protocolGenerator` which carries the symbolProvider. | ||
protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) | ||
} | ||
|
||
/** | ||
* Structure Shape Visitor | ||
* | ||
* For each structure shape, generate: | ||
* - A Rust structure for the shape ([StructureGenerator]). | ||
* - `pyo3::PyClass` trait implementation. | ||
* - A builder for the shape. | ||
* | ||
* This function _does not_ generate any serializers. | ||
*/ | ||
override fun structureShape(shape: StructureShape) { | ||
logger.info("[python-server-codegen] Generating a structure $shape") | ||
rustCrate.useShapeWriter(shape) { writer -> | ||
// Use Python specific structure generator that adds the #[pyclass] attribute | ||
// and #[pymethods] implementation. | ||
PythonServerStructureGenerator(model, codegenContext, symbolProvider, writer, shape).render(CodegenTarget.SERVER) | ||
val builderGenerator = | ||
BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) | ||
builderGenerator.render(writer) | ||
writer.implBlock(shape, symbolProvider) { | ||
builderGenerator.renderConvenienceMethod(this) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Enum Shape Visitor | ||
* | ||
* Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. | ||
*/ | ||
override fun stringShape(shape: StringShape) { | ||
logger.info("[rust-server-codegen] Generating an enum $shape") | ||
shape.getTrait<EnumTrait>()?.also { enum -> | ||
rustCrate.useShapeWriter(shape) { writer -> | ||
PythonServerEnumGenerator(model, symbolProvider, writer, shape, enum, codegenContext.runtimeConfig).render() | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Generate service-specific code for the model: | ||
* - Serializers | ||
* - Deserializers | ||
* - Trait implementations | ||
* - Protocol tests | ||
* - Operation structures | ||
* - Python operation handlers | ||
*/ | ||
override fun serviceShape(shape: ServiceShape) { | ||
logger.info("[python-server-codegen] Generating a service $shape") | ||
PythonServerServiceGenerator( | ||
rustCrate, | ||
protocolGenerator, | ||
protocolGeneratorFactory.support(), | ||
protocolGeneratorFactory.protocol(codegenContext).httpBindingResolver, | ||
codegenContext, | ||
) | ||
.render() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.server.python.smithy | ||
|
||
import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig | ||
import software.amazon.smithy.rust.codegen.smithy.RuntimeType | ||
|
||
/** | ||
* Object used *exclusively* in the runtime of the Python server, for separation concerns. | ||
* Analogous to the companion object in [RuntimeType] and [ServerRuntimeType]; see its documentation for details. | ||
* For a runtime type that is used in the client, or in both the client and the server, use [RuntimeType] directly. | ||
*/ | ||
object PythonServerRuntimeType { | ||
|
||
fun SharedSocket(runtimeConfig: RuntimeConfig) = | ||
RuntimeType("SharedSocket", PythonServerCargoDependency.SmithyHttpServerPython(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server_python") | ||
|
||
fun Blob(runtimeConfig: RuntimeConfig) = | ||
RuntimeType("Blob", PythonServerCargoDependency.SmithyHttpServerPython(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server_python::types") | ||
|
||
fun PyError(runtimeConfig: RuntimeConfig) = | ||
RuntimeType("Error", PythonServerCargoDependency.SmithyHttpServerPython(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server_python") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.server.python.smithy | ||
|
||
import software.amazon.smithy.codegen.core.Symbol | ||
import software.amazon.smithy.model.Model | ||
import software.amazon.smithy.model.shapes.Shape | ||
import software.amazon.smithy.rust.codegen.rustlang.RustMetadata | ||
import software.amazon.smithy.rust.codegen.rustlang.RustType | ||
import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider | ||
import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider | ||
import software.amazon.smithy.rust.codegen.smithy.meta | ||
import software.amazon.smithy.rust.codegen.smithy.rustType | ||
|
||
/** | ||
* Input / output / error structures can refer to complex types like the ones implemented inside | ||
* `aws_smithy_types` (a good example is `aws_smithy_types::Blob`). | ||
* `aws_smithy_http_server_python::types` wraps those types that do not implement directly the | ||
* `pyo3::PyClass` trait and cannot be share safely to Python, providing an idiomatic Python / Rust API. | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* This symbol provider ensures types not implemeting `pyo3::PyClass` are swapped with their wrappers from | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* `aws_smithy_http_server_python::types`. | ||
*/ | ||
class PythonServerSymbolProvider(private val base: RustSymbolProvider, private val model: Model) : | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
WrappingSymbolProvider(base) { | ||
|
||
/** | ||
* Convert a shape to a Symbol. | ||
* | ||
* Swap the symbol if the shape's symbol does not implement `pyo3::PyClass`. | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
override fun toSymbol(shape: Shape): Symbol { | ||
return when (base.toSymbol(shape).fullName) { | ||
"aws_smithy_types::Blob" -> { | ||
buildSymbol("Blob", "aws_smithy_http_server_python::types") | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
else -> { | ||
base.toSymbol(shape) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Create a new symbol based on its name, namespace and metadata. | ||
*/ | ||
private fun buildSymbol(name: String, namespace: String, public: Boolean = false): Symbol = | ||
Symbol.builder() | ||
.name(name) | ||
.namespace(namespace, "::") | ||
.meta(RustMetadata(public = public)) | ||
.rustType(RustType.Opaque(name ?: "", namespace = namespace)).build() | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.server.python.smithy.customizations | ||
|
||
import software.amazon.smithy.rust.codegen.server.smithy.customizations.AddInternalServerErrorToAllOpsDecorator | ||
import software.amazon.smithy.rust.codegen.smithy.CodegenContext | ||
import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator | ||
import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator | ||
import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations | ||
|
||
/** | ||
* Configure the [lib] section to Cargo.toml. | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* [lib] | ||
* name = "$CRATE_NAME" | ||
* crate-type = ["cdylib"] | ||
*/ | ||
class CdylibManifestDecorator : RustCodegenDecorator { | ||
override val name: String = "CdylibDecorator" | ||
override val order: Byte = 0 | ||
|
||
override fun crateManifestCustomizations( | ||
codegenContext: CodegenContext | ||
): ManifestCustomizations = | ||
mapOf("lib" to mapOf("name" to codegenContext.settings.moduleName, "crate-type" to listOf("cdylib"))) | ||
} | ||
|
||
val DECORATORS = listOf( | ||
/** | ||
* Add the [InternalServerError] error to all operations. | ||
* This is done because the Python interpreter can raise exceptions during execution | ||
* and we cannot guarantee infallible execution of operations. | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
AddInternalServerErrorToAllOpsDecorator(), | ||
crisidev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Add the [lib] section to Cargo.toml to configure the generation of the shared library: | ||
CdylibManifestDecorator() | ||
) | ||
|
||
// Combined codegen decorator for Python services. | ||
class PythonServerCodegenDecorator : CombinedCodegenDecorator(DECORATORS) { | ||
override val name: String = "PythonServerCodegenDecorator" | ||
override val order: Byte = -1 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR, but: why did we add this Python subproject inside the
codegen-server-test
subproject? As opposed to have it sitting besides it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preferred to use a subfolder as in my mind Python is just a subproduct of the Rust server codegen.