Skip to content

Commit 5ad44ff

Browse files
committed
Add RST file generation, RST linewrapping, and whitespace fixes
1 parent 7d85d74 commit 5ad44ff

File tree

15 files changed

+923
-67
lines changed

15 files changed

+923
-67
lines changed

codegen/aws/core/src/main/java/software/amazon/smithy/python/aws/codegen/AwsDocConverter.java

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,25 @@ private String convertHtmlToRst(String html) {
4444
Document document = Jsoup.parse(html);
4545
RstNodeVisitor visitor = new RstNodeVisitor();
4646
document.body().traverse(visitor);
47-
return visitor.toString();
47+
return "\n" + visitor;
4848
}
4949

5050
private static class RstNodeVisitor implements NodeVisitor {
5151
private final StringBuilder sb = new StringBuilder();
5252
private boolean inList = false;
53+
private int listDepth = 0;
5354

5455
@Override
5556
public void head(Node node, int depth) {
5657
if (node instanceof TextNode) {
57-
//TODO properly handle stripping whitespace
58-
Node parentNode = node.parent();
59-
if (parentNode != null && parentNode.nodeName().equals("p")) {
60-
//TODO write a test case like the following: <p> Foo <i>bar
61-
// </i> baz</p> -> "Foo *bar* baz"
62-
sb.append(((TextNode) node).text().strip());
63-
} else {
64-
sb.append(((TextNode) node).text());
58+
TextNode textNode = (TextNode) node;
59+
String text = textNode.text();
60+
if (!text.trim().isEmpty()) {
61+
sb.append(text);
62+
// Account for services making a paragraph tag that's empty except
63+
// for a newline
64+
} else if (node.parent() instanceof Element && ((Element) node.parent()).tagName().equals("p")) {
65+
sb.append(text.replaceAll("[ \\t]+", ""));
6566
}
6667
} else if (node instanceof Element) {
6768
Element element = (Element) node;
@@ -78,24 +79,22 @@ public void head(Node node, int depth) {
7879
sb.append("*");
7980
break;
8081
case "code":
81-
sb.append(" ``");
82+
sb.append("``");
8283
break;
8384
case "important":
84-
sb.append(".. important::\n\n ");
85+
sb.append("\n.. important::\n ");
8586
break;
8687
case "note":
87-
sb.append(".. note::\n\n ");
88+
sb.append("\n.. note::\n ");
8889
break;
89-
//TODO this looks a little weird on modelid for invoke_model input
90-
// do I do something weird based on if it's in a parameter cause
91-
// those are already bullets?
9290
case "ul":
9391
inList = true;
92+
listDepth++;
9493
sb.append("\n");
9594
break;
9695
case "li":
9796
if (inList) {
98-
sb.append("- ");
97+
sb.append(" ".repeat(listDepth - 1)).append("* ");
9998
}
10099
break;
101100
}
@@ -119,26 +118,29 @@ public void tail(Node node, int depth) {
119118
sb.append("*");
120119
break;
121120
case "code":
122-
sb.append("`` ");
121+
sb.append("``");
123122
break;
124123
case "important":
125124
case "note":
126-
sb.append("\n\n");
125+
sb.append("\n");
127126
break;
128127
case "ul":
129-
inList = false;
130-
sb.append("\n");
128+
listDepth--;
129+
if (listDepth == 0) {
130+
inList = false;
131+
}
132+
sb.append("\n\n");
131133
break;
132134
case "p":
133-
sb.append("\n\n");
135+
sb.append("\n");
134136
break;
135137
}
136138
}
137139
}
138140

139141
@Override
140142
public String toString() {
141-
return sb.toString().trim();
143+
return sb.toString();
142144
}
143145
}
144146
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.python.aws.codegen;
6+
7+
import software.amazon.smithy.model.traits.InputTrait;
8+
import software.amazon.smithy.model.traits.OutputTrait;
9+
import software.amazon.smithy.python.codegen.GenerationContext;
10+
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
11+
import software.amazon.smithy.python.codegen.sections.*;
12+
import software.amazon.smithy.python.codegen.writer.PythonWriter;
13+
import software.amazon.smithy.utils.CodeInterceptor;
14+
import software.amazon.smithy.utils.CodeSection;
15+
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
19+
public class AwsRstDocFileGenerator implements PythonIntegration {
20+
21+
@Override
22+
public List<? extends CodeInterceptor<? extends CodeSection, PythonWriter>> interceptors(
23+
GenerationContext context
24+
) {
25+
return List.of(
26+
new OperationGenerationInterceptor(context),
27+
new StructureGenerationInterceptor(context),
28+
new ErrorGenerationInterceptor(context),
29+
new UnionGenerationInterceptor(context),
30+
new UnionMemberGenerationInterceptor(context)
31+
);
32+
}
33+
34+
/**
35+
* Utility method to generate a header for documentation files.
36+
*
37+
* @param title The title of the section.
38+
* @return A formatted header string.
39+
*/
40+
private static String generateHeader(String title) {
41+
return String.format("%s\n%s\n\n", title, "=".repeat(title.length()));
42+
}
43+
44+
private static final class OperationGenerationInterceptor
45+
implements CodeInterceptor.Appender<OperationSection, PythonWriter> {
46+
47+
private final GenerationContext context;
48+
49+
public OperationGenerationInterceptor(GenerationContext context) {
50+
this.context = context;
51+
}
52+
53+
@Override
54+
public Class<OperationSection> sectionType() {
55+
return OperationSection.class;
56+
}
57+
58+
@Override
59+
public void append(PythonWriter pythonWriter, OperationSection section) {
60+
var operation = section.operation();
61+
var operationSymbol = context.symbolProvider().toSymbol(operation);
62+
var input = context.model().expectShape(operation.getInputShape());
63+
var inputSymbol = context.symbolProvider().toSymbol(input);
64+
var output = context.model().expectShape(operation.getOutputShape());
65+
var outputSymbol = context.symbolProvider().toSymbol(output);
66+
67+
String operationName = operationSymbol.getName();
68+
String inputSymbolName = inputSymbol.toString();
69+
String outputSymbolName = outputSymbol.toString();
70+
String serviceName = context.symbolProvider().toSymbol(section.service()).getName();
71+
String docsFileName = String.format("docs/client/%s.rst", operationName);
72+
String fullOperationReference = String.format("%s.client.%s.%s",
73+
context.settings().moduleName(),
74+
serviceName,
75+
operationName);
76+
77+
context.writerDelegator().useFileWriter(docsFileName, "", fileWriter -> {
78+
fileWriter.write(generateHeader(operationName));
79+
fileWriter.write(".. automethod:: " + fullOperationReference + "\n\n");
80+
fileWriter.write(".. toctree::\n :hidden:\n :maxdepth: 2\n\n");
81+
fileWriter.write("=================\nInput:\n=================\n\n");
82+
fileWriter.write(".. autoclass:: " + inputSymbolName + "\n :members:\n");
83+
fileWriter.write("=================\nOutput:\n=================\n\n");
84+
fileWriter.write(".. autoclass:: " + outputSymbolName + "\n :members:\n");
85+
});
86+
}
87+
}
88+
89+
private static final class StructureGenerationInterceptor
90+
implements CodeInterceptor.Appender<StructureSection, PythonWriter> {
91+
92+
private final GenerationContext context;
93+
94+
public StructureGenerationInterceptor(GenerationContext context) {
95+
this.context = context;
96+
}
97+
98+
@Override
99+
public Class<StructureSection> sectionType() {
100+
return StructureSection.class;
101+
}
102+
103+
@Override
104+
public void append(PythonWriter pythonWriter, StructureSection section) {
105+
var shape = section.structure();
106+
var symbol = context.symbolProvider().toSymbol(shape);
107+
String docsFileName = String.format("docs/models/%s.rst",
108+
symbol.getName());
109+
if (!shape.hasTrait(InputTrait.class) && !shape.hasTrait(OutputTrait.class)) {
110+
context.writerDelegator().useFileWriter(docsFileName, "", writer -> {
111+
writer.write(generateHeader(symbol.getName()));
112+
writer.write(".. autoclass:: " + symbol.toString() + "\n :members:\n");
113+
});
114+
}
115+
}
116+
}
117+
118+
private static final class ErrorGenerationInterceptor
119+
implements CodeInterceptor.Appender<ErrorSection, PythonWriter> {
120+
121+
private final GenerationContext context;
122+
123+
public ErrorGenerationInterceptor(GenerationContext context) {
124+
this.context = context;
125+
}
126+
127+
@Override
128+
public Class<ErrorSection> sectionType() {
129+
return ErrorSection.class;
130+
}
131+
132+
@Override
133+
public void append(PythonWriter pythonWriter, ErrorSection section) {
134+
var symbol = section.errorSymbol();
135+
String docsFileName = String.format("docs/models/%s.rst",
136+
symbol.getName());
137+
context.writerDelegator().useFileWriter(docsFileName, "", writer -> {
138+
writer.write(generateHeader(symbol.getName()));
139+
writer.write(".. autoexception:: " + symbol.toString() + "\n :members:\n :show-inheritance:\n");
140+
});
141+
}
142+
}
143+
144+
private static final class UnionGenerationInterceptor
145+
implements CodeInterceptor.Appender<UnionSection, PythonWriter> {
146+
147+
private final GenerationContext context;
148+
149+
public UnionGenerationInterceptor(GenerationContext context) {
150+
this.context = context;
151+
}
152+
153+
@Override
154+
public Class<UnionSection> sectionType() {
155+
return UnionSection.class;
156+
}
157+
158+
@Override
159+
public void append(PythonWriter pythonWriter, UnionSection section) {
160+
String parentName = section.parentName();
161+
ArrayList<String> memberNames = section.memberNames();
162+
String docsFileName = String.format("docs/models/%s.rst", parentName);
163+
context.writerDelegator().useFileWriter(docsFileName, "", writer -> {
164+
writer.write(".. _" + parentName + ":\n\n");
165+
writer.write(generateHeader(parentName));
166+
writer.write(".. autodata:: " + context.symbolProvider().toSymbol(section.unionShape()).toString() + " \n");
167+
});
168+
}
169+
}
170+
171+
private static final class UnionMemberGenerationInterceptor
172+
implements CodeInterceptor.Appender<UnionMemberSection, PythonWriter> {
173+
174+
private final GenerationContext context;
175+
176+
public UnionMemberGenerationInterceptor(GenerationContext context) {
177+
this.context = context;
178+
}
179+
180+
@Override
181+
public Class<UnionMemberSection> sectionType() {
182+
return UnionMemberSection.class;
183+
}
184+
185+
@Override
186+
public void append(PythonWriter pythonWriter, UnionMemberSection section) {
187+
var memberSymbol = section.memberSymbol();
188+
String symbolName = memberSymbol.getName();
189+
String docsFileName = String.format("docs/models/%s.rst", symbolName);
190+
context.writerDelegator().useFileWriter(docsFileName, "", writer -> {
191+
writer.write(".. _" + symbolName + ":\n\n");
192+
writer.write(generateHeader(symbolName));
193+
writer.write(".. autoclass:: " + memberSymbol.toString() + " \n");
194+
});
195+
}
196+
}
197+
}

codegen/aws/core/src/main/resources/META-INF/services/software.amazon.smithy.python.codegen.integrations.PythonIntegration

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ software.amazon.smithy.python.aws.codegen.AwsProtocolsIntegration
88
software.amazon.smithy.python.aws.codegen.AwsServiceIdIntegration
99
software.amazon.smithy.python.aws.codegen.AwsUserAgentIntegration
1010
software.amazon.smithy.python.aws.codegen.AwsStandardRegionalEndpointsIntegration
11+
software.amazon.smithy.python.aws.codegen.AwsDocConverter
12+
software.amazon.smithy.python.aws.codegen.AwsRstDocFileGenerator

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@
2323
import software.amazon.smithy.model.traits.StringTrait;
2424
import software.amazon.smithy.python.codegen.integrations.PythonIntegration;
2525
import software.amazon.smithy.python.codegen.integrations.RuntimeClientPlugin;
26-
import software.amazon.smithy.python.codegen.sections.InitializeHttpAuthParametersSection;
27-
import software.amazon.smithy.python.codegen.sections.ResolveEndpointSection;
28-
import software.amazon.smithy.python.codegen.sections.ResolveIdentitySection;
29-
import software.amazon.smithy.python.codegen.sections.SendRequestSection;
30-
import software.amazon.smithy.python.codegen.sections.SignRequestSection;
26+
import software.amazon.smithy.python.codegen.sections.*;
3127
import software.amazon.smithy.python.codegen.writer.PythonWriter;
3228
import software.amazon.smithy.utils.SmithyInternalApi;
3329

@@ -763,6 +759,7 @@ private void generateOperation(PythonWriter writer, OperationShape operation) {
763759
var output = model.expectShape(operation.getOutputShape());
764760
var outputSymbol = symbolProvider.toSymbol(output);
765761

762+
writer.pushState(new OperationSection(service, operation));
766763
writer.openBlock("async def $L(self, input: $T, plugins: list[$T] | None = None) -> $T:",
767764
"",
768765
operationMethodSymbol.getName(),
@@ -790,26 +787,29 @@ private void generateOperation(PythonWriter writer, OperationShape operation) {
790787
""", serSymbol, deserSymbol, operation.getId().getName());
791788
}
792789
});
790+
writer.popState();
793791
}
794792

795793
private void writeSharedOperationInit(PythonWriter writer, OperationShape operation, Shape input) {
796794
writer.writeDocs(() -> {
797-
var docs = operation.getTrait(DocumentationTrait.class)
795+
var docs = writer.formatDocs(operation.getTrait(DocumentationTrait.class)
798796
.map(StringTrait::getValue)
799-
.orElse(String.format("Invokes the %s operation.", operation.getId().getName()));
797+
.orElse(String.format("Invokes the %s operation.",
798+
operation.getId().getName())));
800799

801800
var inputDocs = input.getTrait(DocumentationTrait.class)
802801
.map(StringTrait::getValue)
803802
.orElse("The operation's input.");
804803

805804
writer.write("""
806-
$L
807-
808805
:param input: $L
809806
810807
:param plugins: A list of callables that modify the configuration dynamically.
811-
Changes made by these plugins only apply for the duration of the operation
812-
execution and will not affect any other operation invocations.""", docs, inputDocs);
808+
Changes made by these plugins only apply for the duration of the operation
809+
execution and will not affect any other operation invocations.
810+
811+
$L
812+
""",inputDocs, docs);
813813
});
814814

815815
var defaultPlugins = new LinkedHashSet<SymbolReference>();

0 commit comments

Comments
 (0)