Skip to content
Open
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
2 changes: 1 addition & 1 deletion codegen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@

allprojects {
group = "software.amazon.smithy.python"
version = "0.3.0"
version = "0.3.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -763,12 +763,15 @@ public Void nullNode(NullNode node) {

@Override
public Void numberNode(NumberNode node) {
// TODO: Add support for timestamp, int-enum, and others
if (inputShape.isTimestampShape()) {
var parsed = CodegenUtils.parseTimestampNode(model, inputShape, node);
writer.writeInline(CodegenUtils.getDatetimeConstructor(writer, parsed));
} else if (inputShape.isFloatShape() || inputShape.isDoubleShape()) {
writer.writeInline("float($L)", node.getValue());
} else if (inputShape.isIntEnumShape()) {
var enumSymbol =
context.symbolProvider().toSymbol(inputShape).expectProperty(SymbolProperties.ENUM_SYMBOL);
writer.writeInline("$T($L)", enumSymbol, node.getValue());
} else {
writer.writeInline("$L", node.getValue());
}
Expand Down Expand Up @@ -800,6 +803,10 @@ public Void stringNode(StringNode node) {
};

writer.writeInline("float($S)", value);
} else if (inputShape.isEnumShape()) {
var enumSymbol =
context.symbolProvider().toSymbol(inputShape).expectProperty(SymbolProperties.ENUM_SYMBOL);
writer.writeInline("$T($S)", enumSymbol, node.getValue());
} else {
writer.writeInline("$S", node.getValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,15 +329,9 @@ public Symbol intEnumShape(IntEnumShape shape) {
}

private Symbol genericEnum(Shape shape) {
var enumSymbol = createGeneratedSymbolBuilder(shape, getDefaultShapeName(shape), SHAPES_FILE).build();

// We add this enum symbol as a property on a generic string/int symbol
// rather than returning the enum symbol directly because we only
// generate the enum constants for convenience. We actually want
// to pass around plain types rather than what is effectively
// a namespace class.
return createSymbolBuilder(shape, shape.isEnumShape() ? "str" : "int")
.putProperty(SymbolProperties.ENUM_SYMBOL, escaper.escapeSymbol(shape, enumSymbol))
Symbol symbol = createGeneratedSymbolBuilder(shape, getDefaultShapeName(shape), SHAPES_FILE).build();
return symbol.toBuilder()
.putProperty(SymbolProperties.ENUM_SYMBOL, escaper.escapeSymbol(shape, symbol))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,23 @@
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* Renders enums.
* Renders enums as a {@code StrEnum} subclass.
*
* <p>Beyond the named members, the generated class has:
* <ul>
* <li>{@code is_unknown} — public. {@code True} when the value didn't come
* from a known member: either the service returned a value newer than
* this SDK or client error correction filled in a placeholder.</li>
* <li>{@code _missing_} — invoked when deserializing a value the SDK
* doesn't recognize.</li>
* <li>{@code _unknown} — invoked from {@link MemberErrorCorrectionGenerator}
* to fill a missing required member.</li>
* <li>{@code __eq__} / {@code __hash__} — overridden so an unknown value
* is not equal to any known member, even if its underlying string
* happens to match one.</li>
* </ul>
*
* @see <a href="https://smithy.io/2.0/spec/simple-types.html#enum">Smithy spec: enum</a>
*/
@SmithyInternalApi
public final class EnumGenerator implements Runnable {
Expand All @@ -30,6 +46,7 @@ public void run() {
var enumSymbol = context.symbolProvider().toSymbol(shape).expectProperty(SymbolProperties.ENUM_SYMBOL);
context.writerDelegator().useShapeWriter(shape, writer -> {
writer.addStdlibImport("enum", "StrEnum");
writer.addStdlibImport("typing", "Self");
writer.openBlock("class $L(StrEnum):", "", enumSymbol.getName(), () -> {
shape.getTrait(DocumentationTrait.class).ifPresent(trait -> {
writer.writeDocs(trait.getValue(), context);
Expand All @@ -43,6 +60,43 @@ public void run() {
writer.writeDocs(trait.getValue(), context);
});
}

writer.write("""

@classmethod
def _unknown(cls, value: str) -> "Self":
pseudo = str.__new__(cls, value)
pseudo._name_ = f"<smithy-unknown:{value}>"
pseudo._value_ = value
return pseudo

@classmethod
def _missing_(cls, value: object) -> "Self | None":
if isinstance(value, str):
return cls._unknown(value)
return None

@property
def is_unknown(self) -> bool:
\"""True if this value was not known at SDK generation time.\"""
return self._name_ not in type(self).__members__

def __eq__(self, other: object) -> bool:
if self.is_unknown:
return (
isinstance(other, type(self))
and other.is_unknown
and self._value_ == other._value_
)
if isinstance(other, type(self)) and other.is_unknown:
return False
return super().__eq__(other)

def __hash__(self) -> int:
if self.is_unknown:
return hash(("<smithy-unknown>", type(self).__name__, self._value_))
return super().__hash__()
""");
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@
import software.amazon.smithy.python.codegen.SymbolProperties;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
* Renders intEnums as an {@code IntEnum} subclass.
*
* <p>Beyond the named members, the generated class has:
* <ul>
* <li>{@code is_unknown} — public. {@code True} when the value didn't come
* from a known member: either the service returned a value newer than
* this SDK or client error correction filled in a placeholder.</li>
* <li>{@code _missing_} — invoked when deserializing a value the SDK
* doesn't recognize.</li>
* <li>{@code _unknown} — invoked from {@link MemberErrorCorrectionGenerator}
* to fill a missing required member.</li>
* <li>{@code __eq__} / {@code __hash__} — overridden so an unknown value
* is not equal to any known member, even if its underlying integer
* happens to match one.</li>
* </ul>
*
* @see <a href="https://smithy.io/2.0/spec/simple-types.html#intenum">Smithy spec: intEnum</a>
*/
@SmithyInternalApi
public final class IntEnumGenerator implements Runnable {

Expand All @@ -27,6 +46,7 @@ public void run() {
var enumSymbol = directive.symbol().expectProperty(SymbolProperties.ENUM_SYMBOL);
directive.context().writerDelegator().useShapeWriter(directive.shape(), writer -> {
writer.addStdlibImport("enum", "IntEnum");
writer.addStdlibImport("typing", "Self");
writer.openBlock("class $L(IntEnum):", "", enumSymbol.getName(), () -> {
directive.shape().getTrait(DocumentationTrait.class).ifPresent(trait -> {
writer.writeDocs(trait.getValue(), directive.context());
Expand All @@ -35,11 +55,48 @@ public void run() {
for (MemberShape member : directive.shape().members()) {
var name = directive.symbolProvider().toMemberName(member);
var value = member.expectTrait(EnumValueTrait.class).expectIntValue();
writer.write("$L = $L\n", name, value);
writer.write("$L = $L", name, value);
member.getTrait(DocumentationTrait.class).ifPresent(trait -> {
writer.writeDocs(trait.getValue(), directive.context());
});
}

writer.write("""

@classmethod
def _unknown(cls, value: int) -> "Self":
pseudo = int.__new__(cls, value)
pseudo._name_ = f"<smithy-unknown:{value}>"
pseudo._value_ = value
return pseudo

@classmethod
def _missing_(cls, value: object) -> "Self | None":
if isinstance(value, int):
return cls._unknown(value)
return None

@property
def is_unknown(self) -> bool:
\"""True if this value was not known at SDK generation time.\"""
return self._name_ not in type(self).__members__

def __eq__(self, other: object) -> bool:
if self.is_unknown:
return (
isinstance(other, type(self))
and other.is_unknown
and self._value_ == other._value_
)
if isinstance(other, type(self)) and other.is_unknown:
return False
return super().__eq__(other)

def __hash__(self) -> int:
if self.is_unknown:
return hash(("<smithy-unknown>", type(self).__name__, self._value_))
return super().__hash__()
""");
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,11 @@ public Void integerShape(IntegerShape shape) {

@Override
public Void intEnumShape(IntEnumShape shape) {
writeDeserializer("integer");
pushMemberState();
var enumSymbol = context.symbolProvider().toSymbol(shape).expectProperty(SymbolProperties.ENUM_SYMBOL);
writer.write("$T(${deserializer:L}.read_integer(${C|}))",
enumSymbol,
writer.consumer(w -> writeSchema()));
return null;
}

Expand Down Expand Up @@ -183,7 +187,11 @@ public Void stringShape(StringShape shape) {

@Override
public Void enumShape(EnumShape shape) {
writeDeserializer("string");
pushMemberState();
var enumSymbol = context.symbolProvider().toSymbol(shape).expectProperty(SymbolProperties.ENUM_SYMBOL);
writer.write("$T(${deserializer:L}.read_string(${C|}))",
enumSymbol,
writer.consumer(w -> writeSchema()));
return null;
}

Expand Down
Loading
Loading