Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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)
                       ]))
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
 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()
Ejemplo n.º 5
0
 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()
Ejemplo n.º 6
0
 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()))
Ejemplo n.º 7
0
    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()