Skip to content

Commit c814497

Browse files
authored
Improve service detection during example loading. (#145)
* Improve service detection during exmaple loading. Hold off on verifying known_examples until all doc_gen has been loaded, that is, put it off to the validation phase. More fallback defaults for title. A couple bugs along the way. * Catch missing example services in validate pass * Deprecate add_services * Remove unused
1 parent 593603b commit c814497

File tree

7 files changed

+70
-63
lines changed

7 files changed

+70
-63
lines changed

aws_doc_sdk_examples_tools/categories.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def evaluate(
8888
defaults = field(self.defaults or empty_title_info)
8989
if defaults:
9090
return fake_gotmpl(defaults, service, action)
91-
return ""
91+
return f"{service} {action}"
9292

9393
def validate(self, errors: MetadataErrors):
9494
if not self.display:

aws_doc_sdk_examples_tools/doc_gen.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ def merge(self, other: "DocGen") -> MetadataErrors:
121121
for name, service in other.services.items():
122122
if name not in self.services:
123123
self.services[name] = service
124+
else:
124125
warnings.append(
125126
DocGenMergeWarning(
126127
file=other.root, id=f"conflict in service {name}"
@@ -129,6 +130,7 @@ def merge(self, other: "DocGen") -> MetadataErrors:
129130
for name, snippet in other.snippets.items():
130131
if name not in self.snippets:
131132
self.snippets[name] = snippet
133+
else:
132134
warnings.append(
133135
DocGenMergeWarning(
134136
file=other.root, id=f"conflict in snippet {name}"
@@ -177,7 +179,7 @@ def clone(self) -> "DocGen":
177179
root=self.root,
178180
validation=self.validation.clone(),
179181
sdks={**self.sdks},
180-
entities=self.entities,
182+
entities={**self.entities},
181183
services={**self.services},
182184
errors=MetadataErrors(),
183185
snippets={},
@@ -277,7 +279,6 @@ def process_metadata(self, path: Path) -> "DocGen":
277279
self.standard_categories,
278280
self.cross_blocks,
279281
self.validation,
280-
self.root,
281282
)
282283
self.extend_examples(examples, self.errors)
283284
self.errors.extend(errs)
@@ -310,7 +311,7 @@ def validate(self):
310311
for category in self.categories.values():
311312
category.validate(self.errors)
312313
for example in self.examples.values():
313-
example.validate(self.errors, self.root)
314+
example.validate(self.errors, self.services, self.root)
314315
validate_metadata(self.root, self.validation.strict_titles, self.errors)
315316
validate_no_duplicate_api_examples(self.examples.values(), self.errors)
316317
validate_snippets(
@@ -323,22 +324,25 @@ def validate(self):
323324

324325
def fill_missing_fields(self):
325326
for example in self.examples.values():
327+
id_service, id_action = example.id.split("_", 1)
326328
service_id = example.service_main or next(
327329
(k for (k, _) in example.services.items()), None
328330
)
329331
if service_id is None:
330332
# TODO Log and find which tributaries this effects, as it was supposed to be caught by validations.
331-
continue
333+
service_id = id_service
332334
action = (
333335
next((k for k in example.services.get(service_id, [])), None)
334336
or example.id.split("_", 1)[1]
335337
)
336338
if action is None:
337339
# TODO Log and find which tributaries this effects, as it was supposed to be caught by validations.
338-
continue
339-
example.fill_display_fields(
340-
self.categories, self.services[service_id].short, action
341-
)
340+
action = id_action
341+
if service_id in self.services:
342+
service_name = self.services[service_id].short
343+
else:
344+
service_name = service_id
345+
example.fill_display_fields(self.categories, service_name, action)
342346

343347
def stats(self):
344348
values = self.examples.values()
@@ -402,14 +406,13 @@ def parse_examples(
402406
standard_categories: List[str],
403407
blocks: Set[str],
404408
validation: Optional[ValidationConfig],
405-
root: Optional[Path] = None,
406409
) -> Tuple[List[Example], MetadataErrors]:
407410
examples: List[Example] = []
408411
errors = MetadataErrors()
409412
validation = validation or ValidationConfig()
410413
for id in yaml:
411414
example, example_errors = example_from_yaml(
412-
yaml[id], sdks, services, blocks, validation, root or file.parent
415+
yaml[id], sdks, services, blocks, validation
413416
)
414417
if example.category in standard_categories:
415418
check_id_format(

aws_doc_sdk_examples_tools/metadata.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from collections import defaultdict
88
from dataclasses import dataclass, field
9-
from typing import Dict, Literal, List, Optional, Set, Iterable
9+
from typing import Container, Dict, Literal, List, Optional, Set, Iterable
1010
from os.path import splitext
1111
from pathlib import Path
1212

@@ -55,7 +55,6 @@ class Version:
5555
excerpts: List[Excerpt] = field(default_factory=list)
5656
# Link to the source code for this example. TODO rename.
5757
github: Optional[str] = field(default=None)
58-
add_services: Dict[str, Set[str]] = field(default_factory=dict)
5958
# Deprecated. Replace with guide_topic list.
6059
sdkguide: Optional[str] = field(default=None)
6160
# Link to additional topic places.
@@ -209,8 +208,17 @@ def merge(self, other: Example, errors: MetadataErrors):
209208
err.other_file = other.file # type: ignore
210209
errors.extend(merge_errs)
211210

212-
def validate(self, errors: MetadataErrors, root: Path):
211+
def validate(
212+
self, errors: MetadataErrors, known_services: Container[str], root: Path
213+
):
213214
errs = MetadataErrors()
215+
for service in self.services.keys():
216+
if service not in known_services:
217+
errors.append(
218+
metadata_errors.UnknownService(
219+
id=self.id, file=self.file, service=service
220+
)
221+
)
214222
for language in self.languages.values():
215223
language.validate(errs, root)
216224
for error in errs:

aws_doc_sdk_examples_tools/metadata_errors.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
from dataclasses import dataclass, field
88
from pathlib import Path
9-
from typing import Optional, Iterator, Iterable, List, TypeVar, Generic
9+
from typing import Optional, Iterator, Iterable, List, TypeVar, Generic, Dict, Set
1010

1111

1212
ErrorT = TypeVar("ErrorT")
@@ -267,11 +267,11 @@ def message(self):
267267

268268

269269
@dataclass
270-
class APIExampleCannotAddService(SdkVersionError):
270+
class AddServicesHasBeenDeprecated(SdkVersionError):
271+
add_services: Dict[str, Set[str]] = field(default_factory=dict)
272+
271273
def message(self):
272-
return (
273-
"is an API example but lists additional services in the add_service field."
274-
)
274+
return "lists additional services in add_services, which has been deprecated."
275275

276276

277277
@dataclass

aws_doc_sdk_examples_tools/metadata_test.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,6 @@ def test_verify_load_successful():
484484
github="test_path",
485485
block_content="test block",
486486
excerpts=[],
487-
add_services={},
488487
sdkguide=None,
489488
more_info=[],
490489
),
@@ -498,7 +497,6 @@ def test_verify_load_successful():
498497
Version(
499498
sdk_version=3,
500499
block_content=None,
501-
add_services={"s3": set()},
502500
excerpts=[
503501
Excerpt(
504502
description="Descriptive",
@@ -533,7 +531,6 @@ def test_verify_load_successful():
533531
snippet_files=["snippet_file.txt"],
534532
)
535533
],
536-
add_services={},
537534
more_info=[],
538535
)
539536
],
@@ -646,11 +643,12 @@ def test_verify_load_successful():
646643
language="Perl",
647644
sdk_version=1,
648645
),
649-
metadata_errors.APIExampleCannotAddService(
646+
metadata_errors.AddServicesHasBeenDeprecated(
650647
file=ERRORS_METADATA_PATH,
651648
id="sqs_WrongServiceSlug",
652649
language="Perl",
653650
sdk_version=1,
651+
add_services={"sqs": set()},
654652
),
655653
metadata_errors.ServiceNameFormat(
656654
file=ERRORS_METADATA_PATH,
@@ -677,11 +675,6 @@ def test_verify_load_successful():
677675
# sdk_version=2,
678676
# tag="this.snippet.does.not.exist",
679677
# ),
680-
metadata_errors.UnknownService(
681-
file=ERRORS_METADATA_PATH,
682-
id="medical-imaging_TestExample2",
683-
service="garbled",
684-
),
685678
metadata_errors.FieldError(
686679
file=ERRORS_METADATA_PATH,
687680
id="medical-imaging_TestExample2",
@@ -696,6 +689,13 @@ def test_verify_load_successful():
696689
language="Java",
697690
sdk_version=2,
698691
),
692+
metadata_errors.AddServicesHasBeenDeprecated(
693+
file=ERRORS_METADATA_PATH,
694+
id="cross_TestExample_Versions",
695+
language="Java",
696+
sdk_version=2,
697+
add_services={"sqs": set()},
698+
),
699699
metadata_errors.MissingCrossContent(
700700
file=ERRORS_METADATA_PATH,
701701
id="cross_TestExample_Missing",
@@ -723,6 +723,11 @@ def test_verify_load_successful():
723723
link="perl/example_code/medical-imaging",
724724
root=TEST_RESOURCES_PATH,
725725
),
726+
metadata_errors.UnknownService(
727+
file=ERRORS_METADATA_PATH,
728+
id="medical-imaging_TestExample2",
729+
service="garbled",
730+
),
726731
metadata_errors.InvalidGithubLink(
727732
file=ERRORS_METADATA_PATH,
728733
id="medical-imaging_TestExample2",
@@ -739,12 +744,12 @@ def test_verify_load_successful():
739744
file=FORMATTER_METADATA_PATH,
740745
id="WrongNameFormat",
741746
),
742-
metadata_errors.UnknownService(
747+
metadata_errors.AddServicesHasBeenDeprecated(
743748
file=FORMATTER_METADATA_PATH,
744749
id="cross_TestExample",
745750
language="Java",
746751
sdk_version=2,
747-
service="garbage",
752+
add_services={"garbage": set()},
748753
),
749754
],
750755
[],
@@ -761,7 +766,7 @@ def test_common_errors(
761766
assert expected_errors == [*actual]
762767
validations = MetadataErrors()
763768
for example in examples:
764-
example.validate(validations, root.parent)
769+
example.validate(validations, DOC_GEN.services, root.parent)
765770
assert validation_errors == [*validations]
766771

767772

@@ -976,6 +981,10 @@ def test_no_duplicate_title_abbrev():
976981
services={"svc": set(), "cvs": set()},
977982
),
978983
},
984+
services={
985+
"svc": Service(long="Service", short="svc", version="1", sort="svc"),
986+
"cvs": Service(long="CVS", short="cvs", version="2", sort="cvs"),
987+
},
979988
)
980989
doc_gen.validate()
981990

aws_doc_sdk_examples_tools/test_resources/valid_metadata.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ medical-imaging_TestExample:
2424
genai: some
2525
snippet_tags:
2626
- medical-imaging.JavaScript.datastore.createDatastoreV3
27-
add_services:
28-
s3:
2927
PHP:
3028
versions:
3129
- sdk_version: 3

aws_doc_sdk_examples_tools/yaml_mapper.py

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def example_from_yaml(
2424
services: Dict[str, Service],
2525
blocks: Set[str],
2626
validation: ValidationConfig,
27-
root: Path,
2827
) -> Tuple[Example, MetadataErrors]:
2928
errors = MetadataErrors()
3029

@@ -39,7 +38,7 @@ def example_from_yaml(
3938
errors.append(guide_topic)
4039
guide_topic = None
4140

42-
parsed_services = parse_services(yaml.get("services", {}), errors, services)
41+
parsed_services = parse_services(yaml.get("services", {}), errors)
4342
category = yaml.get("category", "")
4443
if category == "":
4544
category = "Api" if len(parsed_services) == 1 else "Scenarios"
@@ -84,7 +83,7 @@ def example_from_yaml(
8483
else:
8584
for name in yaml_languages:
8685
language, errs = language_from_yaml(
87-
name, yaml_languages[name], sdks, services, blocks, is_action, root
86+
name, yaml_languages[name], sdks, blocks, is_action
8887
)
8988
languages[language.name] = language
9089
errors.extend(errs)
@@ -145,10 +144,8 @@ def language_from_yaml(
145144
name: str,
146145
yaml: Any,
147146
sdks: Dict[str, Sdk],
148-
services: Dict[str, Service],
149147
blocks: Set[str],
150148
is_action: bool,
151-
root: Path,
152149
) -> Tuple[Language, MetadataErrors]:
153150
errors = MetadataErrors()
154151
if name not in sdks:
@@ -164,9 +161,7 @@ def language_from_yaml(
164161

165162
versions: List[Version] = []
166163
for version in yaml_versions:
167-
vers, version_errors = version_from_yaml(
168-
version, services, blocks, is_action, root
169-
)
164+
vers, version_errors = version_from_yaml(version, blocks, is_action)
170165
errors.extend(version_errors)
171166
versions.append(vers)
172167

@@ -177,26 +172,21 @@ def language_from_yaml(
177172
return Language(name, property, versions), errors
178173

179174

180-
def parse_services(
181-
yaml: Any, errors: MetadataErrors, known_services: Dict[str, Service]
182-
) -> Dict[str, Set[str]]:
175+
def parse_services(yaml: Any, errors: MetadataErrors) -> Dict[str, Set[str]]:
183176
if yaml is None:
184177
return {}
185178
services: Dict[str, Set[str]] = {}
186179
for name in yaml:
187-
if name not in known_services:
188-
errors.append(metadata_errors.UnknownService(service=name))
189-
else:
190-
service: Union[Dict[str, None], Set[str], None] = yaml.get(name)
191-
# While .get replaces missing with {}, `sqs: ` in yaml parses a literal `None`
192-
if service is None:
193-
service = set()
194-
if isinstance(service, dict):
195-
service = set(service.keys())
196-
if isinstance(service, set):
197-
# Make a copy of the set for ourselves
198-
service = set(service)
199-
services[name] = set(service)
180+
service: Union[Dict[str, None], Set[str], None] = yaml.get(name)
181+
# While .get replaces missing with {}, `sqs: ` in yaml parses a literal `None`
182+
if service is None:
183+
service = set()
184+
if isinstance(service, dict):
185+
service = set(service.keys())
186+
if isinstance(service, set):
187+
# Make a copy of the set for ourselves
188+
service = set(service)
189+
services[name] = set(service)
200190
return services
201191

202192

@@ -216,10 +206,8 @@ def url_from_yaml(
216206

217207
def version_from_yaml(
218208
yaml: Dict[str, Any],
219-
services: Dict[str, Service],
220209
cross_content_blocks: Set[str],
221210
is_action: bool,
222-
root: Path,
223211
) -> Tuple["Version", MetadataErrors]:
224212
errors = MetadataErrors()
225213

@@ -255,9 +243,11 @@ def version_from_yaml(
255243
elif url is not None:
256244
errors.append(url)
257245

258-
add_services = parse_services(yaml.get("add_services", {}), errors, services)
259-
if add_services and is_action:
260-
errors.append(metadata_errors.APIExampleCannotAddService())
246+
add_services = parse_services(yaml.get("add_services", {}), errors)
247+
if add_services:
248+
errors.append(
249+
metadata_errors.AddServicesHasBeenDeprecated(add_services=add_services)
250+
)
261251

262252
if block_content is not None and block_content not in cross_content_blocks:
263253
errors.append(metadata_errors.MissingCrossContent(block=block_content))
@@ -272,7 +262,6 @@ def version_from_yaml(
272262
block_content,
273263
excerpts,
274264
github,
275-
add_services,
276265
sdkguide,
277266
more_info,
278267
),

0 commit comments

Comments
 (0)