def _generate_struct_class_custom_annotations(self, ns, data_type): """ The _process_custom_annotations function allows client code to access custom annotations defined in the spec. """ self.emit( 'def _process_custom_annotations(self, annotation_type, field_path, processor):' ) with self.indent(), emit_pass_if_nothing_emitted(self): self.emit(( 'super({}, self)._process_custom_annotations(annotation_type, field_path, ' 'processor)').format(class_name_for_data_type(data_type))) self.emit() for field in data_type.fields: field_name = fmt_var(field.name, check_reserved=True) for annotation_type, processor in self._generate_custom_annotation_processors( ns, field.data_type, field.custom_annotations): annotation_class = class_name_for_annotation_type( annotation_type, ns) self.emit( 'if annotation_type is {}:'.format(annotation_class)) with self.indent(): self.emit('self.{} = {}'.format( field_name, generate_func_call( processor, args=[ "'{{}}.{}'.format(field_path)".format( field_name), 'self.{}'.format(field_name), ]))) self.emit()
def _generate_custom_annotation_processors(self, ns, data_type, extra_annotations=()): """ Generates code that will run a custom function 'processor' on every field with a custom annotation, no matter how deep (recursively) it might be located in data_type (incl. in elements of lists or maps). If extra_annotations is passed, it's assumed to be a list of custom annotation applied directly onto data_type (e.g. because it's a field in a struct). Yields pairs of (annotation_type, code) where code is code that evaluates to a function that should be executed with an instance of data_type as the only parameter, and whose return value should replace that instance. """ # annotations applied to members of this type dt, _, _ = unwrap(data_type) if is_struct_type(dt) or is_union_type(dt): annotation_types_seen = set() for annotation in get_custom_annotations_recursive(dt): if annotation.annotation_type not in annotation_types_seen: yield (annotation.annotation_type, generate_func_call( 'bb.make_struct_annotation_processor', args=[ class_name_for_annotation_type( annotation.annotation_type, ns), 'processor' ])) annotation_types_seen.add(annotation.annotation_type) elif is_list_type(dt): for annotation_type, recursive_processor in self._generate_custom_annotation_processors( ns, dt.data_type): # every member needs to be replaced---use handwritten processor yield (annotation_type, generate_func_call('bb.make_list_annotation_processor', args=[recursive_processor])) elif is_map_type(dt): for annotation_type, recursive_processor in self._generate_custom_annotation_processors( ns, dt.value_data_type): # every value needs to be replaced---use handwritten processor yield (annotation_type, generate_func_call( 'bb.make_map_value_annotation_processor', args=[recursive_processor])) # annotations applied directly to this type (through aliases or # passed in from the caller) for annotation in itertools.chain( get_custom_annotations_for_alias(data_type), extra_annotations): yield (annotation.annotation_type, generate_func_call( 'bb.partially_apply', args=[ 'processor', self._generate_custom_annotation_instance( ns, annotation) ]))
def _generate_annotation_type_class(self, ns, annotation_type): # type: (ApiNamespace, AnnotationType) -> None """Defines a Python class that represents an annotation type in Stone.""" self.emit('class {}(bb.AnnotationType):'.format( class_name_for_annotation_type(annotation_type, ns))) with self.indent(): if annotation_type.has_documented_type_or_params(): self.emit('"""') if annotation_type.doc: self.emit_wrapped_text( self.process_doc(annotation_type.doc, self._docf)) if annotation_type.has_documented_params(): self.emit() for param in annotation_type.params: if not param.doc: continue self.emit_wrapped_text(':ivar {}: {}'.format( fmt_var(param.name, True), self.process_doc(param.doc, self._docf)), subsequent_prefix=' ') self.emit('"""') self.emit() self._generate_annotation_type_class_slots(annotation_type) self._generate_annotation_type_class_init(ns, annotation_type) self._generate_annotation_type_class_properties( ns, annotation_type) self.emit()
def _generate_annotation_type_class(self, ns, annotation_type): # type: (ApiNamespace, AnnotationType) -> None """Defines a Python class that represents an annotation type in Stone.""" self.emit('class {}(object):'.format(class_name_for_annotation_type(annotation_type, ns))) with self.indent(): self._generate_annotation_type_class_init(ns, annotation_type) self._generate_annotation_type_class_properties(ns, annotation_type) self.emit()
def _generate_annotation_type_class(self, ns, annotation_type): # type: (ApiNamespace, AnnotationType) -> None """Defines a Python class that represents an annotation type in Stone.""" self.emit('class {}(object):'.format(class_name_for_annotation_type(annotation_type, ns))) with self.indent(): self._generate_annotation_type_class_init(ns, annotation_type) self._generate_annotation_type_class_properties(ns, annotation_type) self.emit()
def _generate_custom_annotation_instance(self, ns, annotation): """ Generates code to construct an instance of an annotation type object with parameters from the specified annotation. """ annotation_class = class_name_for_annotation_type( annotation.annotation_type, ns) return generate_func_call( annotation_class, kwargs=((fmt_var(k, True), self._generate_python_value(ns, v)) for k, v in annotation.kwargs.items()))
def _generate_union_class_custom_annotations(self, ns, data_type): """ The _process_custom_annotations function allows client code to access custom annotations defined in the spec. """ self.emit( 'def _process_custom_annotations(self, annotation_type, field_path, processor):' ) with self.indent(), emit_pass_if_nothing_emitted(self): self.emit(( 'super({}, self)._process_custom_annotations(annotation_type, field_path, ' 'processor)').format(class_name_for_data_type(data_type))) self.emit() for field in data_type.fields: recursive_processors = list( self._generate_custom_annotation_processors( ns, field.data_type, field.custom_annotations)) # check if we have any annotations that apply to this field at all if len(recursive_processors) == 0: continue field_name = fmt_func(field.name) self.emit('if self.is_{}():'.format(field_name)) with self.indent(): for annotation_type, processor in recursive_processors: annotation_class = class_name_for_annotation_type( annotation_type, ns) self.emit('if annotation_type is {}:'.format( annotation_class)) with self.indent(): self.emit('self._value = {}'.format( generate_func_call( processor, args=[ "'{{}}.{}'.format(field_path)".format( field_name), 'self._value', ]))) self.emit()