1414
1515import jinja2
1616import yaml
17+ import itertools
1718import re
1819import os
20+ import typing
1921from typing import Any , DefaultDict , Dict , Mapping
2022from hashlib import sha256
2123from collections import OrderedDict , defaultdict
@@ -107,12 +109,12 @@ def get_response(
107109 template_name , api_schema = api_schema , opts = opts )
108110 )
109111
110- sample_output = self . _generate_samples_and_manifest (
111- api_schema ,
112- self ._env .get_template (sample_templates [0 ]),
113- ) if sample_templates else {}
114-
115- output_files .update (sample_output )
112+ if sample_templates :
113+ sample_output = self . _generate_samples_and_manifest (
114+ api_schema , self ._env .get_template (sample_templates [0 ]),
115+ opts = opts ,
116+ )
117+ output_files .update (sample_output )
116118
117119 # Return the CodeGeneratorResponse output.
118120 res = CodeGeneratorResponse (
@@ -121,12 +123,13 @@ def get_response(
121123 return res
122124
123125 def _generate_samples_and_manifest (
124- self , api_schema : api .API , sample_template : jinja2 .Template ,
125- ) -> Dict [str , CodeGeneratorResponse .File ]:
126+ self , api_schema : api .API , sample_template : jinja2 .Template , * , opts : Options ) -> Dict :
126127 """Generate samples and samplegen manifest for the API.
127128
128129 Arguments:
129130 api_schema (api.API): The schema for the API to which the samples belong.
131+ sample_template (jinja2.Template): The template to use to generate samples.
132+ opts (Options): Additional generator options.
130133
131134 Returns:
132135 Dict[str, CodeGeneratorResponse.File]: A dict mapping filepath to rendered file.
@@ -137,56 +140,50 @@ def _generate_samples_and_manifest(
137140 id_to_hash_to_spec : DefaultDict [str ,
138141 Dict [str , Any ]] = defaultdict (dict )
139142
140- STANDALONE_TYPE = "standalone"
141- for config_fpath in self ._sample_configs :
142- with open (config_fpath ) as f :
143- configs = yaml .safe_load_all (f .read ())
144-
145- spec_generator = (
146- spec
147- for cfg in configs
148- if is_valid_sample_cfg (cfg )
149- for spec in cfg .get ("samples" , [])
150- # If unspecified, assume a sample config describes a standalone.
151- # If sample_types are specified, standalone samples must be
152- # explicitly enabled.
153- if STANDALONE_TYPE in spec .get ("sample_type" , [STANDALONE_TYPE ])
154- )
143+ # Autogenerated sample specs
144+ autogen_specs : typing .List [typing .Dict [str , Any ]] = []
145+ if opts .autogen_snippets :
146+ autogen_specs = list (
147+ samplegen .generate_sample_specs (api_schema , opts = opts ))
148+
149+ # Also process any handwritten sample specs
150+ handwritten_specs = samplegen .parse_handwritten_specs (
151+ self ._sample_configs )
152+
153+ sample_specs = autogen_specs + list (handwritten_specs )
154+
155+ for spec in sample_specs :
156+ # Every sample requires an ID. This may be provided
157+ # by a samplegen config author.
158+ # If no ID is provided, fall back to the region tag.
159+ #
160+ # Ideally the sample author should pick a descriptive, unique ID,
161+ # but this may be impractical and can be error-prone.
162+ spec_hash = sha256 (str (spec ).encode ("utf8" )).hexdigest ()[:8 ]
163+ sample_id = spec .get ("id" ) or spec .get ("region_tag" ) or spec_hash
164+ spec ["id" ] = sample_id
155165
156- for spec in spec_generator :
157- # Every sample requires an ID, preferably provided by the
158- # samplegen config author.
159- # If no ID is provided, fall back to the region tag.
160- # If there's no region tag, generate a unique ID.
161- #
162- # Ideally the sample author should pick a descriptive, unique ID,
163- # but this may be impractical and can be error-prone.
164- spec_hash = sha256 (str (spec ).encode ("utf8" )).hexdigest ()[:8 ]
165- sample_id = spec .get ("id" ) or spec .get (
166- "region_tag" ) or spec_hash
167- spec ["id" ] = sample_id
168-
169- hash_to_spec = id_to_hash_to_spec [sample_id ]
170- if spec_hash in hash_to_spec :
171- raise DuplicateSample (
172- f"Duplicate samplegen spec found: { spec } " )
173-
174- hash_to_spec [spec_hash ] = spec
175-
176- out_dir = "samples"
166+ hash_to_spec = id_to_hash_to_spec [sample_id ]
167+
168+ if spec_hash in hash_to_spec :
169+ raise DuplicateSample (
170+ f"Duplicate samplegen spec found: { spec } " )
171+
172+ hash_to_spec [spec_hash ] = spec
173+
174+ out_dir = "samples/generated_samples"
177175 fpath_to_spec_and_rendered = {}
178176 for hash_to_spec in id_to_hash_to_spec .values ():
179177 for spec_hash , spec in hash_to_spec .items ():
180178 id_is_unique = len (hash_to_spec ) == 1
181- # The ID is used to generate the file name and by sample tester
182- # to link filenames to invoked samples. It must be globally unique.
179+ # The ID is used to generate the file name. It must be globally unique.
183180 if not id_is_unique :
184181 spec ["id" ] += f"_{ spec_hash } "
185182
186183 sample = samplegen .generate_sample (
187184 spec , api_schema , sample_template ,)
188185
189- fpath = spec ["id" ] + ".py"
186+ fpath = utils . to_snake_case ( spec ["id" ]) + ".py"
190187 fpath_to_spec_and_rendered [os .path .join (out_dir , fpath )] = (
191188 spec ,
192189 sample ,
@@ -199,20 +196,24 @@ def _generate_samples_and_manifest(
199196 for fname , (_ , sample ) in fpath_to_spec_and_rendered .items ()
200197 }
201198
202- # Only generate a manifest if we generated samples.
203- if output_files :
204- manifest_fname , manifest_doc = manifest .generate (
205- (
206- (fname , spec )
207- for fname , (spec , _ ) in fpath_to_spec_and_rendered .items ()
208- ),
209- api_schema ,
210- )
211-
212- manifest_fname = os .path .join (out_dir , manifest_fname )
213- output_files [manifest_fname ] = CodeGeneratorResponse .File (
214- content = manifest_doc .render (), name = manifest_fname
215- )
199+ # TODO(busunkim): Re-enable manifest generation once metadata
200+ # format has been formalized.
201+ # https://docs.google.com/document/d/1ghBam8vMj3xdoe4xfXhzVcOAIwrkbTpkMLgKc9RPD9k/edit#heading=h.sakzausv6hue
202+ #
203+ # if output_files:
204+
205+ # manifest_fname, manifest_doc = manifest.generate(
206+ # (
207+ # (fname, spec)
208+ # for fname, (spec, _) in fpath_to_spec_and_rendered.items()
209+ # ),
210+ # api_schema,
211+ # )
212+
213+ # manifest_fname = os.path.join(out_dir, manifest_fname)
214+ # output_files[manifest_fname] = CodeGeneratorResponse.File(
215+ # content=manifest_doc.render(), name=manifest_fname
216+ # )
216217
217218 return output_files
218219
0 commit comments