Skip to content

Commit 8bced95

Browse files
authored
Fix for optional dependencies of SymbolDependencies (#412)
1 parent c06dd6d commit 8bced95

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,15 @@ private static List<ConfigProperty> getHttpProperties(GenerationContext context)
138138
if (usesHttp2(context)) {
139139
clientBuilder
140140
.initialize(writer -> {
141-
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
142-
writer.addDependency(SmithyPythonDependency.AWS_CRT);
141+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP.withOptionalDependencies("awscrt"));
143142
writer.addImport("smithy_http.aio.crt", "AWSCRTHTTPClient");
144143
writer.write("self.http_client = http_client or AWSCRTHTTPClient()");
145144
});
146145

147146
} else {
148147
clientBuilder
149148
.initialize(writer -> {
150-
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP);
151-
writer.addDependency(SmithyPythonDependency.AIO_HTTP);
149+
writer.addDependency(SmithyPythonDependency.SMITHY_HTTP.withOptionalDependencies("aiohttp"));
152150
writer.addImport("smithy_http.aio.aiohttp", "AIOHTTPClient");
153151
writer.write("self.http_client = http_client or AIOHTTPClient()");
154152
});

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

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
import java.util.List;
1010
import java.util.Map;
1111
import java.util.Optional;
12+
import java.util.TreeMap;
13+
import java.util.function.BinaryOperator;
14+
import java.util.function.Function;
15+
import java.util.stream.Collectors;
16+
import java.util.stream.Stream;
17+
import software.amazon.smithy.codegen.core.CodegenException;
1218
import software.amazon.smithy.codegen.core.SymbolDependency;
1319
import software.amazon.smithy.codegen.core.WriterDelegator;
1420
import software.amazon.smithy.model.traits.DocumentationTrait;
@@ -30,17 +36,70 @@
3036
@SmithyInternalApi
3137
public final class SetupGenerator {
3238

33-
private SetupGenerator() {}
39+
private SetupGenerator() {
40+
}
3441

3542
public static void generateSetup(
3643
PythonSettings settings,
3744
GenerationContext context
3845
) {
39-
var dependencies = SymbolDependency.gatherDependencies(context.writerDelegator().getDependencies().stream());
46+
var dependencies = gatherDependencies(context.writerDelegator().getDependencies().stream());
4047
writePyproject(settings, context.writerDelegator(), dependencies);
4148
writeReadme(settings, context);
4249
}
4350

51+
/**
52+
* Merge all the symbol dependencies. Also merges optional dependencies.
53+
* Modification of : SymbolDependency.gatherDependencies that also considers the OPTIONAL_DEPENDENCIES
54+
* property.
55+
*/
56+
@SuppressWarnings("unchecked")
57+
private static Map<String, Map<String, SymbolDependency>> gatherDependencies(
58+
Stream<SymbolDependency> symbolStream
59+
) {
60+
BinaryOperator<SymbolDependency> guardedMergeWithProperties = (a, b) -> {
61+
if (!a.getVersion().equals(b.getVersion())) {
62+
throw new CodegenException(String.format(
63+
"Found a conflicting `%s` dependency for `%s`: `%s` conflicts with `%s`",
64+
a.getDependencyType(),
65+
a.getPackageName(),
66+
a.getVersion(),
67+
b.getVersion()));
68+
}
69+
// For our purposes, we need only consider OPTIONAL_DEPENDENCIES property.
70+
// The only other property currently used is IS_LINK, and it is consistent across all usages of
71+
// a given SymbolDependency.
72+
if (!b.getTypedProperties().isEmpty()) {
73+
var optional_a = a.getProperty(SymbolProperties.OPTIONAL_DEPENDENCIES).orElse(List.of());
74+
var optional_b = b.getProperty(SymbolProperties.OPTIONAL_DEPENDENCIES).orElse(List.of());
75+
76+
if (optional_b.isEmpty()) {
77+
return a;
78+
}
79+
80+
if (optional_a.isEmpty()) {
81+
return b;
82+
}
83+
84+
var merged = Stream.concat(optional_a.stream(), optional_b.stream())
85+
.distinct()
86+
.toList();
87+
88+
return a.toBuilder()
89+
.putProperty(SymbolProperties.OPTIONAL_DEPENDENCIES, merged)
90+
.build();
91+
} else {
92+
return a;
93+
}
94+
};
95+
return symbolStream.sorted()
96+
.collect(Collectors.groupingBy(SymbolDependency::getDependencyType,
97+
Collectors.toMap(SymbolDependency::getPackageName,
98+
Function.identity(),
99+
guardedMergeWithProperties,
100+
TreeMap::new)));
101+
}
102+
44103
/**
45104
* Write a pyproject.toml file.
46105
*
@@ -64,7 +123,7 @@ private static void writePyproject(
64123
[build-system]
65124
requires = ["setuptools", "setuptools-scm", "wheel"]
66125
build-backend = "setuptools.build_meta"
67-
126+
68127
[project]
69128
name = $1S
70129
version = $2S
@@ -100,18 +159,18 @@ private static void writePyproject(
100159
writer.write("""
101160
[tool.setuptools.packages.find]
102161
exclude=["tests*"]
103-
162+
104163
[tool.pyright]
105164
typeCheckingMode = "strict"
106165
reportPrivateUsage = false
107166
reportUnusedFunction = false
108167
reportUnusedVariable = false
109168
reportUnnecessaryComparison = false
110169
reportUnusedClass = false
111-
170+
112171
[tool.black]
113172
target-version = ["py311"]
114-
173+
115174
[tool.pytest.ini_options]
116175
python_classes = ["!Test"]
117176
asyncio_mode = "auto"
@@ -122,17 +181,17 @@ private static void writePyproject(
122181
}
123182

124183
private static void writeDependencyList(PythonWriter writer, Collection<SymbolDependency> dependencies) {
125-
for (var iter = dependencies.iterator(); iter.hasNext();) {
184+
for (var iter = dependencies.iterator(); iter.hasNext(); ) {
126185
writer.pushState();
127186
var dependency = iter.next();
128187
writer.putContext("deps", getOptionalDependencies(dependency));
129188
writer.putContext("isLink", dependency.getProperty(SymbolProperties.IS_LINK).orElse(false));
130189
writer.putContext("last", !iter.hasNext());
131190
writer.write("""
132-
"$L\
133-
${?deps}[${#deps}${value:L}${^key.last}, ${/key.last}${/deps}]${/deps}\
134-
${?isLink} @ ${/isLink}$L"\
135-
${^last},${/last}""",
191+
"$L\
192+
${?deps}[${#deps}${value:L}${^key.last}, ${/key.last}${/deps}]${/deps}\
193+
${?isLink} @ ${/isLink}$L"\
194+
${^last},${/last}""",
136195
dependency.getPackageName(),
137196
dependency.getVersion());
138197
writer.popState();
@@ -152,7 +211,7 @@ private static List<String> getOptionalDependencies(SymbolDependency dependency)
152211
})
153212
.orElse(Collections.emptyList());
154213
try {
155-
return (List<String>) optionals;
214+
return optionals;
156215
} catch (Exception e) {
157216
return Collections.emptyList();
158217
}
@@ -177,7 +236,7 @@ private static void writeReadme(
177236
writer.pushState(new ReadmeSection());
178237
writer.write("""
179238
## $L Client
180-
239+
181240
$L
182241
""", title, description);
183242

@@ -190,7 +249,7 @@ private static void writeReadme(
190249
// since the python code docs are RST format.
191250
writer.write("""
192251
### Documentation
193-
252+
194253
$L
195254
""", documentation);
196255
});

0 commit comments

Comments
 (0)