Skip to content

Commit

Permalink
feat(type-safe-api): upgrade python runtime openapi generator to 7.1.…
Browse files Browse the repository at this point in the history
…0 and support native deps (#663)

Upgrades the generated python runtime to use OpenAPI generator version 7.1.0. This provides the same
interface for models, but upgrades to pydantic v2 under the covers. Since pydantic v2 has native
dependencies, we additionally ensure the lambda distributable can be customised to be built for a
particular architecture. This is only supported for python handler projects at present, and
TypeScript or Java handler projects may need their "package" tasks overridden with custom commands.
  • Loading branch information
cogwirrel authored Nov 30, 2023
1 parent 2c4ff99 commit f3ad66f
Show file tree
Hide file tree
Showing 9 changed files with 3,704 additions and 2,289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,93 @@ As well as generating lambda handler stubs, when you use the `@handler` Smithy t
!!!warning

The `<Operation>Function` constructs will point to the implementation in the project corresponding to the `language` you selected in the `@handler` Smithy trait or `x-handler` OpenAPI vendor extension. If you relocate your handler implementation and leave the trait a new handler stub will be generated and the construct will point to that. If you remove the `@handler` Smithy trait or `x-handler` OpenAPI vendor extension from an operation, your generated CDK infrastructure will not include a CDK function construct, and you will need to write your own.

### Lambda Architectures

AWS Lambda allows you to [specify the instruction set architecture of functions](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html). Since the generated function CDK constructs allow you to configure all properties of the lambda function, you may [configure the `architecture` property](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html#architecture) to target your desired architecture.

The `x86_64` architecture is targetted by default, but you can target `arm64` as follows:

=== "TS"

```ts
new SayHelloFunction(this, "SayHello", {
architecture: Architecture.ARM_64,
});
```

=== "JAVA"

```java
new SayHelloFunction(this, "SayHello", SayHelloFunctionProps.builder()
.architecture(Architecture.ARM_64)
.build());
```

=== "PYTHON"

```python
SayHelloFunction(self, "SayHello",
architecture=Architecture.ARM_64,
)
```

#### Native Dependencies

If your lambda handlers rely on native dependencies, you will need to ensure you target the appropriate architecture when building the lambda distributable. For convenience when using the Python handlers project, you can select your target architecture by configuring your `TypeSafeApiProject`'s handler options in your `.projenrc`, for example:


=== "TS"

```ts
new TypeSafeApiProject({
...
handlers: {
languages: [Language.PYTHON],
options: {
python: {
// Target lambdas in arm64
architecture: Architecture.ARM_64,
}
}
}
});
```

=== "JAVA"

```java
new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()
...
.handlers(HandlersConfiguration.builder()
.languages(Arrays.asList(Language.PYTHON))
.options(GeneratedHandlersCodeOptions.builder()
.python(GeneratedPythonHandlersOptions.builder()
// Target lambdas in arm64
.architecture(Architecture.ARM_64)
.build())
.build())
.build())
.build());
```

=== "PYTHON"

```python

TypeSafeApiProject(
...
handlers=HandlersConfiguration(
languages=[Language.PYTHON],
options=GeneratedHandlersCodeOptions(
python=GeneratedPythonHandlersOptions(
# Target lambdas in arm64
architecture=Architecture.ARM_64
)
)
),
)
```

!!!warning
For TypeScript and Java, you may need to override the `package` task for the handlers project to run the appropriate commands to build your handlers with their native dependencies, or consider consuming them using a [Lambda layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html).
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ class {{operationIdCamelCase}}RequestParameters(BaseModel):
class Config:
"""Pydantic configuration"""
allow_population_by_field_name = True
populate_by_name = True
validate_assignment = True
def to_json(self) -> str:
Expand All @@ -232,13 +232,13 @@ class {{operationIdCamelCase}}RequestParameters(BaseModel):
return cls.from_dict(json.loads(json_str))
def to_dict(self):
return self.dict(exclude={}, exclude_none=True)
return self.model_dump(exclude={}, exclude_none=True)
@classmethod
def from_dict(cls, obj: dict) -> {{operationIdCamelCase}}RequestParameters:
if obj is None:
return None
return {{operationIdCamelCase}}RequestParameters.parse_obj(obj)
return {{operationIdCamelCase}}RequestParameters.model_validate(obj)
# Request body type (default to Any when no body parameters exist, or leave unchanged as str if it's a primitive type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PythonProject } from "projen/lib/python";
import {
CodeGenerationSourceOptions,
GeneratedPythonHandlersOptions,
Architecture,
} from "../../types";
import { OpenApiGeneratorHandlebarsIgnoreFile } from "../components/open-api-generator-handlebars-ignore-file";
import { OpenApiGeneratorIgnoreFile } from "../components/open-api-generator-ignore-file";
Expand Down Expand Up @@ -126,8 +127,14 @@ export class GeneratedPythonHandlersProject extends PythonProject {
this.packageTask.exec(
"poetry export --without-hashes --format=requirements.txt > dist/lambda/requirements.txt"
);
// Select the platform based on the specified architecture, defaulting to x86_64
// See: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-native-libraries
const platform =
options.architecture === Architecture.ARM_64
? "manylinux2014_aarch64"
: "manylinux2014_x86_64";
this.packageTask.exec(
"pip install -r dist/lambda/requirements.txt --target dist/lambda --upgrade"
`pip install -r dist/lambda/requirements.txt --target dist/lambda --upgrade --platform ${platform} --only-binary :all:`
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ export class GeneratedPythonRuntimeProject extends PythonProject {
// Add dependencies required by the client
[
"python-dateutil@~2.8.2",
"pydantic@^1.10.5",
"pydantic@^2.5.2",
"aenum@^3.1.11",
"urllib3@~1.26.7",
`aws-lambda-powertools@{extras=["all"],version="^2.23.0"}`,
`aws-lambda-powertools@{extras=["tracer", "aws-sdk"],version="^2.28.0"}`,
"python@^3.9",
].forEach((dep) => this.addDependency(dep));

Expand All @@ -86,7 +86,7 @@ export class GeneratedPythonRuntimeProject extends PythonProject {

// Add OpenAPI Generator cli configuration
OpenApiToolsJsonFile.ensure(this).addOpenApiGeneratorCliConfig({
version: "6.6.0",
version: "7.1.0",
...options.openApiGeneratorCliConfig,
});

Expand Down Expand Up @@ -123,7 +123,7 @@ export class GeneratedPythonRuntimeProject extends PythonProject {

public buildGenerateCommandArgs = () => {
return buildInvokeOpenApiGeneratorCommandArgs({
generator: "python-nextgen",
generator: "python",
specPath: this.options.specPath,
generatorDirectory: Language.PYTHON,
additionalProperties: {
Expand Down
24 changes: 23 additions & 1 deletion packages/type-safe-api/src/project/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ export enum ModelLanguage {
OPENAPI = "OPENAPI",
}

/**
* Represents an instruction set architecture
*/
export enum Architecture {
/**
* 64-bit x86 architecture
*/
X86_64 = "X86_64",
/**
* 64-bit ARM architecture
*/
ARM_64 = "ARM_64",
}

/**
* Options for a Smithy model
*/
Expand Down Expand Up @@ -231,7 +245,15 @@ export interface GeneratedTypeScriptHandlersOptions
*/
export interface GeneratedPythonHandlersOptions
extends PythonProjectOptions,
GeneratedWithOpenApiGeneratorOptions {}
GeneratedWithOpenApiGeneratorOptions {
/**
* The architecture to target for python handlers.
* This determines the --platform argument passed to the pip install command used to build the lambda distributable.
* @see https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-native-libraries
* @default Architecture.X86_64
*/
readonly architecture?: Architecture;
}

/**
* Options for configuring a generated java handlers project
Expand Down
Loading

0 comments on commit f3ad66f

Please sign in to comment.