Example #1
0
    def _generate_routes_m(self, namespace):
        """Generates implementation file for namespace object that has as methods
        all routes within the namespace."""
        with self.block_m(
                fmt_routes_class(namespace.name, self.args.auth_type)):
            init_args = fmt_func_args_declaration([(
                'client', 'id<{}>'.format(self.args.transport_client_name))])

            with self.block_func(
                    func='init', args=init_args, return_type='instancetype'):
                self.emit('self = [super init];')
                with self.block_init():
                    self.emit('_client = client;')
            self.emit()
            style_to_request = json.loads(self.args.z__style_to_request)

            for route in namespace.routes:
                if (route.attrs.get('auth') != self.args.auth_type
                        and route.attrs.get('auth') != 'noauth'):
                    continue

                route_type = route.attrs.get('style')
                client_args = json.loads(self.args.client_args)

                if route_type in client_args.keys():
                    for args_data in client_args[route_type]:
                        task_type_key, type_data_dict = tuple(args_data)
                        task_type_name = style_to_request[task_type_key]

                        func_suffix = type_data_dict[0]
                        extra_args = [
                            tuple(type_data[:-1])
                            for type_data in type_data_dict[1]
                        ]

                        if (is_struct_type(route.arg_data_type) and
                                self._struct_has_defaults(route.arg_data_type)):
                            route_args, _ = self._get_default_route_args(
                                namespace, route)
                            self._generate_route_m(route, namespace,
                                                   route_args, extra_args,
                                                   task_type_name, func_suffix)

                        route_args, _ = self._get_route_args(namespace, route)
                        self._generate_route_m(route, namespace, route_args,
                                               extra_args, task_type_name,
                                               func_suffix)
                else:
                    task_type_name = style_to_request[route_type]
                    if (is_struct_type(route.arg_data_type) and
                            self._struct_has_defaults(route.arg_data_type)):
                        route_args, _ = self._get_default_route_args(
                            namespace, route)
                        self._generate_route_m(route, namespace, route_args,
                                               [], task_type_name, '')

                    route_args, _ = self._get_route_args(namespace, route)
                    self._generate_route_m(route, namespace, route_args, [],
                                           task_type_name, '')
Example #2
0
    def _generate_union_serializer(self, data_type):
        with self.serializer_block(data_type):
            with self.serializer_func(data_type), self.block('switch value'):
                for field in data_type.all_fields:
                    field_type = field.data_type
                    case = '.{}{}'.format(fmt_class(field.name),
                                         '' if is_void_type(field_type) else '(let arg)')
                    self.emit('case {}:'.format(case))

                    with self.indent():
                        if is_void_type(field_type):
                            self.emit('var d = [String: JSON]()')
                        elif (is_struct_type(field_type) and
                                not field_type.has_enumerated_subtypes()):
                            self.emit('var d = Serialization.getFields({}.serialize(arg))'.format(
                                fmt_serial_obj(field_type)))
                        else:
                            self.emit('var d = ["{}": {}.serialize(arg)]'.format(
                                field.name,
                                fmt_serial_obj(field_type)))
                        self.emit('d[".tag"] = .Str("{}")'.format(field.name))
                        self.emit('return .Dictionary(d)')
            with self.deserializer_func(data_type):
                with self.block("switch json"):
                    self.emit("case .Dictionary(let d):")
                    with self.indent():
                        self.emit('let tag = Serialization.getTag(d)')
                        with self.block('switch tag'):
                            for field in data_type.all_fields:
                                field_type = field.data_type
                                self.emit('case "{}":'.format(field.name))

                                tag_type = self._tag_type(data_type, field)
                                with self.indent():
                                    if is_void_type(field_type):
                                        self.emit('return {}'.format(tag_type))
                                    else:
                                        if (is_struct_type(field_type) and
                                                not field_type.has_enumerated_subtypes()):
                                            subdict = 'json'
                                        else:
                                            subdict = 'd["{}"] ?? .Null'.format(field.name)

                                        self.emit('let v = {}.deserialize({})'.format(
                                            fmt_serial_obj(field_type), subdict
                                        ))
                                        self.emit('return {}(v)'.format(tag_type))
                            self.emit('default:')
                            with self.indent():
                                if data_type.catch_all_field:
                                    self.emit('return {}'.format(
                                        self._tag_type(data_type, data_type.catch_all_field)
                                    ))
                                else:
                                    self.emit('fatalError("Unknown tag \(tag)")')
                    self.emit("default:")
                    with self.indent():

                        self.emit('fatalError("Failed to deserialize")')
Example #3
0
    def _generate_union_serializer(self, data_type):
        with self.serializer_block(data_type):
            with self.serializer_func(data_type), self.block('switch value'):
                for field in data_type.all_fields:
                    field_type = field.data_type
                    case = '.{}{}'.format(fmt_var(field.name),
                                         '' if is_void_type(field_type) else '(let arg)')
                    self.emit('case {}:'.format(case))

                    with self.indent():
                        if is_void_type(field_type):
                            self.emit('var d = [String: JSON]()')
                        elif (is_struct_type(field_type) and
                                not field_type.has_enumerated_subtypes()):
                            self.emit('var d = Serialization.getFields({}.serialize(arg))'.format(
                                fmt_serial_obj(field_type)))
                        else:
                            self.emit('var d = ["{}": {}.serialize(arg)]'.format(
                                field.name,
                                fmt_serial_obj(field_type)))
                        self.emit('d[".tag"] = .str("{}")'.format(field.name))
                        self.emit('return .dictionary(d)')
            with self.deserializer_func(data_type):
                with self.block("switch json"):
                    self.emit("case .dictionary(let d):")
                    with self.indent():
                        self.emit('let tag = Serialization.getTag(d)')
                        with self.block('switch tag'):
                            for field in data_type.all_fields:
                                field_type = field.data_type
                                self.emit('case "{}":'.format(field.name))

                                tag_type = self._tag_type(data_type, field)
                                with self.indent():
                                    if is_void_type(field_type):
                                        self.emit('return {}'.format(tag_type))
                                    else:
                                        if (is_struct_type(field_type) and
                                                not field_type.has_enumerated_subtypes()):
                                            subdict = 'json'
                                        else:
                                            subdict = 'd["{}"] ?? .null'.format(field.name)

                                        self.emit('let v = {}.deserialize({})'.format(
                                            fmt_serial_obj(field_type), subdict
                                        ))
                                        self.emit('return {}(v)'.format(tag_type))
                            self.emit('default:')
                            with self.indent():
                                if data_type.catch_all_field:
                                    self.emit('return {}'.format(
                                        self._tag_type(data_type, data_type.catch_all_field)
                                    ))
                                else:
                                    self.emit('fatalError("Unknown tag \(tag)")')
                    self.emit("default:")
                    with self.indent():

                        self.emit('fatalError("Failed to deserialize")')
Example #4
0
    def _generate_base_namespace_module(self, api, namespace):
        """Creates a module for the namespace. All data types and routes are
        represented as Python classes."""

        self.cur_namespace = namespace
        self.emit('# -*- coding: utf-8 -*-')
        self.emit('# Auto-generated by Stone, do not modify.')
        if namespace.doc is not None:
            self.emit('"""')
            self.emit_raw(namespace.doc)
            self.emit('"""')
            self.emit()

        self.emit_raw(validators_import)

        imported_namespaces = namespace.get_imported_namespaces()
        if imported_namespaces:
            # Generate import statements for all referenced namespaces.
            self.emit('try:')
            with self.indent():
                self.emit('from . import (')
                with self.indent():
                    for ns in imported_namespaces:
                        self.emit(ns.name + ',')
                self.emit(')')
            self.emit('except (SystemError, ValueError):')
            # Fallback if imported from outside a package.
            with self.indent():
                for ns in imported_namespaces:
                    self.emit('import %s' % ns.name)
            self.emit()

        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class(namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class(namespace, data_type)
            else:
                raise TypeError('Cannot handle type %r' % type(data_type))

        for alias in namespace.linearize_aliases():
            self._generate_alias_definition(namespace, alias)

        # Generate the struct->subtype tag mapping at the end so that
        # references to later-defined subtypes don't cause errors.
        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class_reflection_attributes(
                    namespace, data_type)
                if data_type.has_enumerated_subtypes():
                    self._generate_enumerated_subtypes_tag_mapping(
                        namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class_reflection_attributes(
                    namespace, data_type)
                self._generate_union_class_symbol_creators(data_type)

        self._generate_routes(api.route_schema, namespace)
Example #5
0
    def _generate_base_namespace_module(self, api, namespace):
        """Creates a module for the namespace. All data types and routes are
        represented as Python classes."""

        self.cur_namespace = namespace
        self.emit('# -*- coding: utf-8 -*-')
        self.emit('# Auto-generated by Stone, do not modify.')
        if namespace.doc is not None:
            self.emit('"""')
            self.emit_raw(namespace.doc)
            self.emit('"""')
            self.emit()

        self.emit_raw(validators_import)

        imported_namespaces = namespace.get_imported_namespaces()
        if imported_namespaces:
            # Generate import statements for all referenced namespaces.
            self.emit('try:')
            with self.indent():
                self.emit('from . import (')
                with self.indent():
                    for ns in imported_namespaces:
                        self.emit(ns.name + ',')
                self.emit(')')
            self.emit('except (SystemError, ValueError):')
            # Fallback if imported from outside a package.
            with self.indent():
                for ns in imported_namespaces:
                    self.emit('import %s' % ns.name)
            self.emit()

        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class(namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class(namespace, data_type)
            else:
                raise TypeError('Cannot handle type %r' % type(data_type))

        for alias in namespace.linearize_aliases():
            self._generate_alias_definition(namespace, alias)

        # Generate the struct->subtype tag mapping at the end so that
        # references to later-defined subtypes don't cause errors.
        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class_reflection_attributes(
                    namespace, data_type)
                if data_type.has_enumerated_subtypes():
                    self._generate_enumerated_subtypes_tag_mapping(
                        namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class_reflection_attributes(
                    namespace, data_type)
                self._generate_union_class_symbol_creators(data_type)

        self._generate_routes(api.route_schema, namespace)
Example #6
0
    def _get_imports_m(self, data_types, default_imports):
        """Emits all necessary implementation file imports for the given Stone data type."""
        if not isinstance(data_types, list):
            data_types = [data_types]

        import_classes = default_imports

        for data_type in data_types:
            import_classes.append(fmt_class_prefix(data_type))

            if data_type.parent_type:
                import_classes.append(fmt_class_prefix(data_type.parent_type))

            if is_struct_type(
                    data_type) and data_type.has_enumerated_subtypes():
                for tags, subtype in data_type.get_all_subtypes_with_tags():
                    import_classes.append(fmt_class_prefix(subtype))

            for field in data_type.all_fields:
                data_type, _ = unwrap_nullable(field.data_type)

                # unpack list
                while is_list_type(data_type):
                    data_type = data_type.data_type

                if is_user_defined_type(data_type):
                    import_classes.append(fmt_class_prefix(data_type))

        if import_classes:
            import_classes = list(set(import_classes))
            import_classes.sort()

        return import_classes
Example #7
0
    def _get_namespace_route_imports(self,
                                     namespace,
                                     include_route_args=True,
                                     include_route_deep_args=False):
        result = []

        def _unpack_and_store_data_type(data_type):
            data_type, _ = unwrap_nullable(data_type)
            if is_list_type(data_type):
                while is_list_type(data_type):
                    data_type = data_type.data_type

            if not is_void_type(data_type) and is_user_defined_type(data_type):
                result.append(data_type)

        for route in namespace.routes:
            if include_route_args:
                data_type, _ = unwrap_nullable(route.arg_data_type)
                _unpack_and_store_data_type(data_type)
            elif include_route_deep_args:
                data_type, _ = unwrap_nullable(route.arg_data_type)
                if is_union_type(data_type) or is_list_type(data_type):
                    _unpack_and_store_data_type(data_type)
                elif not is_void_type(data_type):
                    for field in data_type.all_fields:
                        data_type, _ = unwrap_nullable(field.data_type)
                        if is_struct_type(data_type) or is_union_type(
                                data_type) or is_list_type(data_type):
                            _unpack_and_store_data_type(data_type)

            _unpack_and_store_data_type(route.result_data_type)
            _unpack_and_store_data_type(route.error_data_type)

        return result
Example #8
0
    def block_h_from_data_type(self, data_type, protocol=None):
        assert is_user_defined_type(data_type), \
            'Expected user-defined type, got %r' % type(data_type)

        if not protocol:
            extensions = []
            if data_type.parent_type and is_struct_type(data_type):
                extensions.append(fmt_class_prefix(data_type.parent_type))
            else:
                if is_union_type(data_type):
                    # Use a handwritten base class
                    extensions.append('NSObject')
                else:
                    extensions.append('NSObject')

            extend_suffix = ' : {}'.format(
                ', '.join(extensions)) if extensions else ''
        else:
            base = fmt_class_prefix(data_type.parent_type) if (
                data_type.parent_type
                and not is_union_type(data_type)) else 'NSObject'
            extend_suffix = ' : {} <{}>'.format(base, ', '.join(protocol))
        with self.block('@interface {}{}'.format(fmt_class_prefix(data_type),
                                                 extend_suffix),
                        delim=('', '@end'),
                        dent=0):
            self.emit()
            yield
Example #9
0
    def _get_route_args(self, namespace, route, tag=False):
        """Returns a list of name / value string pairs representing the arguments for
        a particular route."""
        data_type, _ = unwrap_nullable(route.arg_data_type)
        if is_struct_type(data_type):
            arg_list = []
            for field in data_type.all_fields:
                arg_list.append((fmt_var(field.name),
                                 fmt_type(field.data_type,
                                          tag=tag,
                                          has_default=field.has_default)))

            doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf))
                        for f in data_type.fields if f.doc]
        elif is_union_type(data_type):
            arg_list = [(fmt_var(data_type.name),
                         fmt_type(route.arg_data_type, tag=tag))]

            doc_list = [
                (fmt_var(data_type.name),
                 self.process_doc(data_type.doc, self._docf) if data_type.doc
                 else 'The {} union'.format(fmt_class(data_type.name)))
            ]
        else:
            arg_list = []
            doc_list = []

        return arg_list, doc_list
Example #10
0
    def block_h_from_data_type(self, data_type, protocol=None):
        assert is_user_defined_type(data_type), \
            'Expected user-defined type, got %r' % type(data_type)

        if not protocol:
            extensions = []
            if data_type.parent_type and is_struct_type(data_type):
                extensions.append(fmt_class_prefix(data_type.parent_type))
            else:
                if is_union_type(data_type):
                    # Use a handwritten base class
                    extensions.append('NSObject')
                else:
                    extensions.append('NSObject')

            extend_suffix = ' : {}'.format(
                ', '.join(extensions)) if extensions else ''
        else:
            base = fmt_class_prefix(data_type.parent_type) if (
                data_type.parent_type and not is_union_type(data_type)) else 'NSObject'
            extend_suffix = ' : {} <{}>'.format(base, ', '.join(protocol))
        with self.block('@interface {}{}'.format(
                fmt_class_prefix(data_type), extend_suffix), delim=('', '@end'), dent=0):
            self.emit()
            yield
Example #11
0
    def _get_default_route_args(
            self,
            namespace,  # pylint: disable=unused-argument
            route,
            tag=False):
        """Returns a list of name / value string pairs representing the default arguments for
        a particular route."""
        data_type, _ = unwrap_nullable(route.arg_data_type)
        if is_struct_type(data_type):
            arg_list = []
            for field in data_type.all_fields:
                if not field.has_default and not is_nullable_type(
                        field.data_type):
                    arg_list.append(
                        (fmt_var(field.name), fmt_type(field.data_type,
                                                       tag=tag)))

            doc_list = ([(fmt_var(f.name), self.process_doc(f.doc, self._docf))
                         for f in data_type.fields
                         if f.doc and not f.has_default
                         and not is_nullable_type(f.data_type)])
        else:
            arg_list = []
            doc_list = []

        return arg_list, doc_list
Example #12
0
    def _emit_route(self, namespace, route, extra_args=None, extra_docs=None):
        arg_list, doc_list = self._get_route_args(namespace, route)
        extra_args = extra_args or []
        extra_docs = extra_docs or []

        arg_type = self._swift_type_mapping(route.arg_data_type)
        func_name = fmt_func(route.name)

        self.emit('/**')
        if route.doc:
            route_doc = self.process_doc(route.doc, self._docf)
        else:
            route_doc = 'The {} route'.format(func_name)
        self.emit_wrapped_text(route_doc, prefix='    ', width=120)
        self.emit()
        for name, doc in doc_list + extra_docs:
            if not doc:
                doc = 'Undocumented'
            self.emit_wrapped_text('- parameter {}: {}'.format(name, doc), prefix='    ', width=120)
        self.emit()
        self.emit_wrapped_text(' - returns: Through the response callback, the caller will ' +
                               'receive a `{}` object on success or a `{}` object on ' +
                               'failure.'.format(
                                   self._swift_type_mapping(route.result_data_type),
                                   self._swift_type_mapping(route.error_data_type)),
                              prefix='    ', width=120)
        self.emit('*/')
        route_type = self.STYLE_MAPPING[route.attrs.get('style')]

        rtype = self._swift_type_mapping(route.result_data_type,
                                         serializer=True)
        etype = self._swift_type_mapping(route.error_data_type,
                                         serializer=True)

        host_ident = route.attrs.get('host', 'meta')
        func_args = [
            ('client', 'self.client'),
            ('host', '"'+host_ident+'"'),
            ('route', '"/{}/{}"'.format(namespace.name, route.name)),
            ('params', '{}.serialize({})'.format(
                self._serializer_obj(route.arg_data_type),
                '' if is_void_type(route.arg_data_type) else 'request')),
            ('responseSerializer', self._serializer_obj(route.result_data_type)),
            ('errorSerializer', self._serializer_obj(route.error_data_type)),
        ]

        for name, typ, value in extra_args:
            arg_list.append((name, typ))
            func_args.append((name, value))

        with self.function_block('public func {}'.format(func_name),
                                 args=self._func_args(arg_list, force_first=True),
                                 return_type='Stone{}Request<{}, {}>'.format(route_type,
                                                                               rtype,
                                                                               etype)):
            if is_struct_type(route.arg_data_type):
                args = [(name, name) for name, _ in self._struct_init_args(route.arg_data_type)]
                self.emit('let request = {}({})'.format(arg_type, self._func_args(args)))

            self.emit('return Stone{}Request({})'.format(route_type, self._func_args(func_args)))
Example #13
0
    def _get_imports_m(self, data_types, default_imports):
        """Emits all necessary implementation file imports for the given Stone data type."""
        if not isinstance(data_types, list):
            data_types = [data_types]

        import_classes = default_imports

        for data_type in data_types:
            import_classes.append(fmt_class_prefix(data_type))

            if data_type.parent_type:
                import_classes.append(fmt_class_prefix(data_type.parent_type))

            if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
                for _, subtype in data_type.get_all_subtypes_with_tags():
                    import_classes.append(fmt_class_prefix(subtype))

            for field in data_type.all_fields:
                data_type, _ = unwrap_nullable(field.data_type)

                # unpack list
                while is_list_type(data_type):
                    data_type = data_type.data_type

                if is_user_defined_type(data_type):
                    import_classes.append(fmt_class_prefix(data_type))

        if import_classes:
            import_classes = list(set(import_classes))
            import_classes.sort()

        return import_classes
Example #14
0
    def _get_default_route_args(
            self,
            namespace,  # pylint: disable=unused-argument
            route,
            tag=False):
        """Returns a list of name / value string pairs representing the default arguments for
        a particular route."""
        data_type, _ = unwrap_nullable(route.arg_data_type)
        if is_struct_type(data_type):
            arg_list = []
            for field in data_type.all_fields:
                if not field.has_default and not is_nullable_type(
                        field.data_type):
                    arg_list.append((fmt_var(field.name), fmt_type(
                        field.data_type, tag=tag)))

            doc_list = ([(fmt_var(f.name), self.process_doc(f.doc, self._docf))
                         for f in data_type.fields
                         if f.doc and not f.has_default and
                         not is_nullable_type(f.data_type)])
        else:
            arg_list = []
            doc_list = []

        return arg_list, doc_list
Example #15
0
    def _get_route_args(self, namespace, route, tag=False):  # pylint: disable=unused-argument
        """Returns a list of name / value string pairs representing the arguments for
        a particular route."""
        data_type, _ = unwrap_nullable(route.arg_data_type)
        if is_struct_type(data_type):
            arg_list = []
            for field in data_type.all_fields:
                arg_list.append((fmt_var(field.name), fmt_type(
                    field.data_type, tag=tag, has_default=field.has_default)))

            doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf))
                        for f in data_type.fields if f.doc]
        elif is_union_type(data_type):
            arg_list = [(fmt_var(data_type.name), fmt_type(
                route.arg_data_type, tag=tag))]

            doc_list = [(fmt_var(data_type.name),
                self.process_doc(data_type.doc,
                    self._docf) if data_type.doc
                else 'The {} union'.format(
                    fmt_class(data_type
                        .name)))]
        else:
            arg_list = []
            doc_list = []

        return arg_list, doc_list
Example #16
0
    def _get_namespace_route_imports(self,
                                     namespace,
                                     include_route_args=True,
                                     include_route_deep_args=False):
        result = []

        def _unpack_and_store_data_type(data_type):
            data_type, _ = unwrap_nullable(data_type)
            if is_list_type(data_type):
                while is_list_type(data_type):
                    data_type = data_type.data_type

            if not is_void_type(data_type) and is_user_defined_type(data_type):
                result.append(data_type)

        for route in namespace.routes:
            if include_route_args:
                data_type, _ = unwrap_nullable(route.arg_data_type)
                _unpack_and_store_data_type(data_type)
            elif include_route_deep_args:
                data_type, _ = unwrap_nullable(route.arg_data_type)
                if is_union_type(data_type) or is_list_type(data_type):
                    _unpack_and_store_data_type(data_type)
                elif not is_void_type(data_type):
                    for field in data_type.all_fields:
                        data_type, _ = unwrap_nullable(field.data_type)
                        if (is_struct_type(data_type) or
                                is_union_type(data_type) or
                                is_list_type(data_type)):
                            _unpack_and_store_data_type(data_type)

            _unpack_and_store_data_type(route.result_data_type)
            _unpack_and_store_data_type(route.error_data_type)

        return result
    def _generate_union_helper(self, u):
        name = u.name
        namespace = u.namespace
        fields = u.fields
        if is_struct_type(u) and u.has_enumerated_subtypes():
            name = fmt_var(name, export=False) + 'Union'
            fields = u.get_enumerated_subtypes()

        with self.block('type %s struct' % name):
            self.emit('dropbox.Tagged')
            for field in fields:
                if is_void_type(field.data_type):
                    continue
                self._generate_field(field, union_field=True,
                                     namespace=namespace)
        self.emit()
        self.emit('// Valid tag values for %s' % fmt_var(u.name))
        with self.block('const', delim=('(', ')')):
            for field in fields:
                self.emit('%s%s = "%s"' % (fmt_var(u.name), fmt_var(field.name), field.name))
        self.emit()

        num_void_fields = sum([is_void_type(f.data_type) for f in fields])
        # Simple structure, no need in UnmarshalJSON
        if len(fields) == num_void_fields:
            return

        self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
        with self.block('func (u *%s) UnmarshalJSON(body []byte) error' % name):
            with self.block('type wrap struct'):
                self.emit('dropbox.Tagged')
                for field in fields:
                    if is_void_type(field.data_type) or \
                            is_primitive_type(field.data_type):
                        continue
                    self._generate_field(field, union_field=True,
                                         namespace=namespace, raw=True)
            self.emit('var w wrap')
            with self.block('if err := json.Unmarshal(body, &w); err != nil'):
                self.emit('return err')
            self.emit('u.Tag = w.Tag')
            with self.block('switch u.Tag'):
                for field in fields:
                    if is_void_type(field.data_type):
                        continue
                    field_name = fmt_var(field.name)
                    with self.block('case "%s":' % field.name, delim=(None, None)):
                        if is_union_type(field.data_type):
                            with self.block('if err := json.Unmarshal'
                                            '(w.{0}, &u.{0}); err != nil'
                                            .format(field_name)):
                                self.emit('return err')
                        else:
                            with self.block('if err := json.Unmarshal'
                                            '(body, &u.{0}); err != nil'
                                            .format(field_name)):
                                self.emit('return err')
            self.emit('return nil')
        self.emit()
Example #18
0
    def _emit_route(self, namespace, route, req_obj_name, extra_args=None, extra_docs=None):
        arg_list, doc_list = self._get_route_args(namespace, route)
        extra_args = extra_args or []
        extra_docs = extra_docs or []

        arg_type = fmt_type(route.arg_data_type)
        func_name = fmt_func(route.name)

        if route.doc:
            route_doc = self.process_doc(route.doc, self._docf)
        else:
            route_doc = 'The {} route'.format(func_name)
        self.emit_wrapped_text(route_doc, prefix='/// ', width=120)
        self.emit('///')

        for name, doc in doc_list + extra_docs:
            param_doc = '- parameter {}: {}'.format(name, doc if doc is not None else undocumented)
            self.emit_wrapped_text(param_doc, prefix='/// ', width=120)
        self.emit('///')
        output = (' - returns: Through the response callback, the caller will ' +
                         'receive a `{}` object on success or a `{}` object on failure.')
        output = output.format(fmt_type(route.result_data_type),
                               fmt_type(route.error_data_type))
        self.emit_wrapped_text(output, prefix='/// ', width=120)

        func_args = [
            ('route', '{}.{}'.format(fmt_class(namespace.name), func_name)),
        ]
        client_args = []
        return_args = [('route', 'route')]

        for name, value, typ in extra_args:
            arg_list.append((name, typ))
            func_args.append((name, value))
            client_args.append((name, value))

        rtype = fmt_serial_type(route.result_data_type)
        etype = fmt_serial_type(route.error_data_type)

        self._maybe_generate_deprecation_warning(route)

        with self.function_block('public func {}'.format(func_name),
                args=self._func_args(arg_list, force_first=True),
                return_type='{}<{}, {}>'.format(req_obj_name, rtype, etype)):
            self.emit('let route = {}.{}'.format(fmt_class(namespace.name), func_name))
            if is_struct_type(route.arg_data_type):
                args = [(name, name) for name, _ in self._struct_init_args(route.arg_data_type)]
                func_args += [('serverArgs', '{}({})'.format(arg_type, self._func_args(args)))]
                self.emit('let serverArgs = {}({})'.format(arg_type, self._func_args(args)))
            elif is_union_type(route.arg_data_type):
                self.emit('let serverArgs = {}'.format(fmt_var(route.arg_data_type.name)))

            if not is_void_type(route.arg_data_type):
                return_args += [('serverArgs', 'serverArgs')]

            return_args += client_args

            self.emit('return client.request({})'.format(self._func_args(return_args, not_init=True)))
        self.emit()
Example #19
0
def is_ptr_type(data_type):
    data_type, _ = unwrap_nullable(data_type)
    if data_type.__class__ in _true_primitives:
        type_name = 'NSInteger'
    type_name = _primitive_table.get(data_type.__class__,
                                     fmt_class(data_type.name))
    return type_name[-1] == '*' or is_struct_type(data_type) or is_list_type(
        data_type)
Example #20
0
 def _get_route_args(self, namespace, route):
     arg_type = self._swift_type_mapping(route.arg_data_type)
     if is_struct_type(route.arg_data_type):
         arg_list = self._struct_init_args(route.arg_data_type, namespace=namespace)
         doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf))
                     for f in route.arg_data_type.fields if f.doc]
     else:
         arg_list = [] if is_void_type(route.arg_data_type) else [('request', arg_type)]
         doc_list = []
     return arg_list, doc_list
Example #21
0
 def _generate_type(self, data_type, indent_spaces, extra_args):
     """
     Generates a TypeScript type for the given type.
     """
     if is_alias(data_type):
         self._generate_alias_type(data_type)
     elif is_struct_type(data_type):
         self._generate_struct_type(data_type, indent_spaces, extra_args)
     elif is_union_type(data_type):
         self._generate_union_type(data_type, indent_spaces)
 def _generate_data_type(self, data_type):
     generate_doc(self, data_type)
     if is_struct_type(data_type):
         self._generate_struct(data_type)
         if data_type.has_enumerated_subtypes():
             self._generate_base_type(data_type)
     elif is_union_type(data_type):
         self._generate_union(data_type)
     else:
         self.logger.info("Unhandled data type", data_type)
 def _generate_data_type(self, data_type):
     generate_doc(self, data_type)
     if is_struct_type(data_type):
         self._generate_struct(data_type)
         if data_type.has_enumerated_subtypes():
             self._generate_base_type(data_type)
     elif is_union_type(data_type):
         self._generate_union(data_type)
     else:
         self.logger.info("Unhandled data type", data_type)
Example #24
0
    def _generate_base_namespace_module(self, namespace):
        self.emit_raw(base)

        self.emit('/**')
        self.emit('    Datatypes and serializers for the {} namespace'.format(namespace.name))
        self.emit('*/')
        with self.block('public class {}'.format(fmt_class(namespace.name))):
            for data_type in namespace.linearize_data_types():
                if is_struct_type(data_type):
                    self._generate_struct_class(namespace, data_type)
                elif is_union_type(data_type):
                    self._generate_union_type(namespace, data_type)
Example #25
0
def fmt_type(data_type):
    """
    Returns a JSDoc annotation for a data type.
    May contain a union of enumerated subtypes.
    """
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        possible_types = []
        possible_subtypes = data_type.get_all_subtypes_with_tags()
        for _, subtype in possible_subtypes:
            possible_types.append(fmt_type_name(subtype))
        if data_type.is_catch_all():
            possible_types.append(fmt_type_name(data_type))
        return fmt_jsdoc_union(possible_types)
    else:
        return fmt_type_name(data_type)
Example #26
0
def fmt_type(data_type):
    """
    Returns a JSDoc annotation for a data type.
    May contain a union of enumerated subtypes.
    """
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        possible_types = []
        possible_subtypes = data_type.get_all_subtypes_with_tags()
        for _, subtype in possible_subtypes:
            possible_types.append(fmt_type_name(subtype))
        if data_type.is_catch_all():
            possible_types.append(fmt_type_name(data_type))
        return fmt_jsdoc_union(possible_types)
    else:
        return fmt_type_name(data_type)
Example #27
0
    def _generate_base_namespace_module(self, api, namespace):
        self.emit_raw(base)

        routes_base = 'Datatypes and serializers for the {} namespace'.format(namespace.name)
        self.emit_wrapped_text(routes_base, prefix='/// ', width=120)

        with self.block('open class {}'.format(fmt_class(namespace.name))):
            for data_type in namespace.linearize_data_types():
                if is_struct_type(data_type):
                    self._generate_struct_class(namespace, data_type)
                    self.emit()
                elif is_union_type(data_type):
                    self._generate_union_type(namespace, data_type)
                    self.emit()
            if namespace.routes:
                self._generate_route_objects(api.route_schema, namespace)
Example #28
0
    def _generate_base_namespace_module(self, api, namespace):
        self.emit_raw(base)

        routes_base = 'Datatypes and serializers for the {} namespace'.format(namespace.name)
        self.emit_wrapped_text(routes_base, prefix='/// ', width=120)

        with self.block('public class {}'.format(fmt_class(namespace.name))):
            for data_type in namespace.linearize_data_types():
                if is_struct_type(data_type):
                    self._generate_struct_class(namespace, data_type)
                    self.emit()
                elif is_union_type(data_type):
                    self._generate_union_type(namespace, data_type)
                    self.emit()
            if namespace.routes:
                self._generate_route_objects(api.route_schema, namespace)
Example #29
0
    def _generate_base_namespace_module(self, api, namespace):
        """Creates a module for the namespace. All data types and routes are
        represented as Python classes."""

        self.cur_namespace = namespace
        self.emit('# -*- coding: utf-8 -*-')
        self.emit('# Auto-generated by Stone, do not modify.')
        self.emit('# flake8: noqa')
        self.emit('# pylint: skip-file')

        if namespace.doc is not None:
            self.emit('"""')
            self.emit_raw(namespace.doc)
            self.emit('"""')
            self.emit()

        self.emit_raw(validators_import)

        # Generate import statements for all referenced namespaces.
        self._generate_imports_for_referenced_namespaces(namespace)

        for data_type in namespace.linearize_data_types():
            if isinstance(data_type, Struct):
                self._generate_struct_class(namespace, data_type)
            elif isinstance(data_type, Union):
                self._generate_union_class(namespace, data_type)
            else:
                raise TypeError('Cannot handle type %r' % type(data_type))

        for alias in namespace.linearize_aliases():
            self._generate_alias_definition(namespace, alias)

        # Generate the struct->subtype tag mapping at the end so that
        # references to later-defined subtypes don't cause errors.
        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class_reflection_attributes(
                    namespace, data_type)
                if data_type.has_enumerated_subtypes():
                    self._generate_enumerated_subtypes_tag_mapping(
                        namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class_reflection_attributes(
                    namespace, data_type)
                self._generate_union_class_symbol_creators(data_type)

        self._generate_routes(api.route_schema, namespace)
Example #30
0
    def _generate_base_namespace_module(self, api, namespace):
        """Creates a module for the namespace. All data types and routes are
        represented as Python classes."""

        self.cur_namespace = namespace
        self.emit('# -*- coding: utf-8 -*-')
        self.emit('# Auto-generated by Stone, do not modify.')
        self.emit('# flake8: noqa')
        self.emit('# pylint: skip-file')

        if namespace.doc is not None:
            self.emit('"""')
            self.emit_raw(namespace.doc)
            self.emit('"""')
            self.emit()

        self.emit_raw(validators_import)

        # Generate import statements for all referenced namespaces.
        self._generate_imports_for_referenced_namespaces(namespace)

        for data_type in namespace.linearize_data_types():
            if isinstance(data_type, Struct):
                self._generate_struct_class(namespace, data_type)
            elif isinstance(data_type, Union):
                self._generate_union_class(namespace, data_type)
            else:
                raise TypeError('Cannot handle type %r' % type(data_type))

        for alias in namespace.linearize_aliases():
            self._generate_alias_definition(namespace, alias)

        # Generate the struct->subtype tag mapping at the end so that
        # references to later-defined subtypes don't cause errors.
        for data_type in namespace.linearize_data_types():
            if is_struct_type(data_type):
                self._generate_struct_class_reflection_attributes(
                    namespace, data_type)
                if data_type.has_enumerated_subtypes():
                    self._generate_enumerated_subtypes_tag_mapping(
                        namespace, data_type)
            elif is_union_type(data_type):
                self._generate_union_class_reflection_attributes(
                    namespace, data_type)
                self._generate_union_class_symbol_creators(data_type)

        self._generate_routes(api.route_schema, namespace)
Example #31
0
def fmt_type(data_type, inside_namespace=None):
    """
    Returns a TypeScript type annotation for a data type.
    May contain a union of enumerated subtypes.
    inside_namespace should be set to the namespace that the type reference
    occurs in, or None if this parameter is not relevant.
    """
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        possible_types = []
        possible_subtypes = data_type.get_all_subtypes_with_tags()
        for _, subtype in possible_subtypes:
            possible_types.append(fmt_polymorphic_type_reference(subtype, inside_namespace))
        if data_type.is_catch_all():
            possible_types.append(fmt_polymorphic_type_reference(data_type, inside_namespace))
        return fmt_union(possible_types)
    else:
        return fmt_type_name(data_type, inside_namespace)
Example #32
0
    def _get_route_args(self, namespace, route):
        data_type = route.arg_data_type
        arg_type = fmt_type(data_type)
        if is_struct_type(data_type):
            arg_list = self._struct_init_args(data_type, namespace=namespace)

            doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf)
                if f.doc else undocumented) for f in data_type.fields if f.doc]
        elif is_union_type(data_type):
            arg_list = [(fmt_var(data_type.name), '{}.{}'.format(
                fmt_class(namespace.name), fmt_class(data_type.name)))]
            doc_list = [(fmt_var(data_type.name),
                self.process_doc(data_type.doc, self._docf)
                if data_type.doc else 'The {} union'.format(fmt_class(data_type.name)))]
        else:
            arg_list = [] if is_void_type(data_type) else [('request', arg_type)]
            doc_list = []
        return arg_list, doc_list
 def _generate_result(self, route):
     out = self.emit
     if is_struct_type(route.result_data_type) and \
             route.result_data_type.has_enumerated_subtypes():
         out('var tmp %sUnion' % fmt_var(route.result_data_type.name, export=False))
         with self.block('err = json.Unmarshal(body, &tmp);'
                         'if err != nil'):
             out('return')
         with self.block('switch tmp.Tag'):
             for t in route.result_data_type.get_enumerated_subtypes():
                 with self.block('case "%s":' % t.name, delim=(None, None)):
                     self.emit('res = tmp.%s' % fmt_var(t.name))
     elif not is_void_type(route.result_data_type):
         with self.block('err = json.Unmarshal(body, &res);'
                         'if err != nil'):
             out('return')
         out()
     out('return')
Example #34
0
def fmt_type(data_type, inside_namespace=None):
    """
    Returns a TypeScript type annotation for a data type.
    May contain a union of enumerated subtypes.
    inside_namespace should be set to the namespace that the type reference
    occurs in, or None if this parameter is not relevant.
    """
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        possible_types = []
        possible_subtypes = data_type.get_all_subtypes_with_tags()
        for _, subtype in possible_subtypes:
            possible_types.append(
                fmt_polymorphic_type_reference(subtype, inside_namespace))
        if data_type.is_catch_all():
            possible_types.append(
                fmt_polymorphic_type_reference(data_type, inside_namespace))
        return fmt_union(possible_types)
    else:
        return fmt_type_name(data_type, inside_namespace)
Example #35
0
 def _generate_result(self, route):
     out = self.emit
     if is_struct_type(route.result_data_type) and \
             route.result_data_type.has_enumerated_subtypes():
         out('var tmp %sUnion' %
             fmt_var(route.result_data_type.name, export=False))
         with self.block('err = json.Unmarshal(body, &tmp);'
                         'if err != nil'):
             out('return')
         with self.block('switch tmp.Tag'):
             for t in route.result_data_type.get_enumerated_subtypes():
                 with self.block('case "%s":' % t.name, delim=(None, None)):
                     self.emit('res = tmp.%s' % fmt_var(t.name))
     elif not is_void_type(route.result_data_type):
         with self.block('err = json.Unmarshal(body, &res);'
                         'if err != nil'):
             out('return')
         out()
     out('return')
Example #36
0
 def _generate_route_method_decl(self,
                                 namespace,
                                 route,
                                 arg_data_type,
                                 request_binary_body,
                                 method_name_suffix=None,
                                 extra_args=None):
     """Generates the method prototype for a route."""
     method_name = fmt_func(route.name)
     namespace_name = fmt_func(namespace.name)
     if method_name_suffix:
         method_name += method_name_suffix
     args = ['self']
     if extra_args:
         args += extra_args
     if request_binary_body:
         args.append('f')
     if is_struct_type(arg_data_type):
         for field in arg_data_type.all_fields:
             if is_nullable_type(field.data_type):
                 args.append('{}=None'.format(field.name))
             elif field.has_default:
                 # TODO(kelkabany): Decide whether we really want to set the
                 # default in the argument list. This will send the default
                 # over the wire even if it isn't overridden. The benefit is
                 # it locks in a default even if it is changed server-side.
                 if is_user_defined_type(field.data_type):
                     ns = field.data_type.namespace
                 else:
                     ns = None
                 arg = '{}={}'.format(
                     field.name,
                     self._generate_python_value(ns, field.default))
                 args.append(arg)
             else:
                 args.append(field.name)
     elif is_union_type(arg_data_type):
         args.append('arg')
     elif not is_void_type(arg_data_type):
         raise AssertionError('Unhandled request type: %r' % arg_data_type)
     self.generate_multiline_list(
         args, 'def {}_{}'.format(namespace_name, method_name), ':')
Example #37
0
 def _generate_route_method_decl(
         self, namespace, route, arg_data_type, request_binary_body,
         method_name_suffix=None, extra_args=None):
     """Generates the method prototype for a route."""
     method_name = fmt_func(route.name)
     namespace_name = fmt_func(namespace.name)
     if method_name_suffix:
         method_name += method_name_suffix
     args = ['self']
     if extra_args:
         args += extra_args
     if request_binary_body:
         args.append('f')
     if is_struct_type(arg_data_type):
         for field in arg_data_type.all_fields:
             if is_nullable_type(field.data_type):
                 args.append('{}=None'.format(field.name))
             elif field.has_default:
                 # TODO(kelkabany): Decide whether we really want to set the
                 # default in the argument list. This will send the default
                 # over the wire even if it isn't overridden. The benefit is
                 # it locks in a default even if it is changed server-side.
                 if is_user_defined_type(field.data_type):
                     ns = field.data_type.namespace
                 else:
                     ns = None
                 arg = '{}={}'.format(
                     field.name,
                     self._generate_python_value(ns, field.default))
                 args.append(arg)
             else:
                 args.append(field.name)
     elif is_union_type(arg_data_type):
         args.append('arg')
     elif not is_void_type(arg_data_type):
         raise AssertionError('Unhandled request type: %r' %
                              arg_data_type)
     self.generate_multiline_list(
         args, 'def {}_{}'.format(namespace_name, method_name), ':')
Example #38
0
    def generate(self, api):
        # TODO - Make these configurable
        cls_name = 'Dropbox'
        mod_name = 'dropbox'

        with self.output_to_relative_path(self.args.filename):

            # Generate interface definitions for user-defined types
            for namespace in api.namespaces.values():
                for typ in namespace.linearize_data_types():
                    if data_type.is_struct_type(typ):
                        self._generate_interface(typ)
                    elif data_type.is_union_type(typ):
                        self._generate_union_interface(typ)

            # Generate route definition
            with self.block('declare module "{}"'.format(mod_name)):
                with self.block('class {}'.format(cls_name)):
                    for namespace in api.namespaces.values():
                        for route in namespace.routes:
                            self._generate_method(namespace, route)

                self.emit('export = {};'.format(cls_name))
Example #39
0
    def _get_route_args(self, namespace, route):
        data_type = route.arg_data_type
        arg_type = fmt_type(data_type)
        if is_struct_type(data_type):
            arg_list = self._struct_init_args(data_type, namespace=namespace)

            doc_list = [(fmt_var(f.name), self.process_doc(f.doc, self._docf)
                         if f.doc else undocumented) for f in data_type.fields
                        if f.doc]
        elif is_union_type(data_type):
            arg_list = [(fmt_var(data_type.name),
                         '{}.{}'.format(fmt_class(namespace.name),
                                        fmt_class(data_type.name)))]
            doc_list = [
                (fmt_var(data_type.name),
                 self.process_doc(data_type.doc, self._docf) if data_type.doc
                 else 'The {} union'.format(fmt_class(data_type.name)))
            ]
        else:
            arg_list = [] if is_void_type(data_type) else [('request',
                                                            arg_type)]
            doc_list = []
        return arg_list, doc_list
Example #40
0
    def _generate_namespace_module(self, namespace):
        for data_type in namespace.linearize_data_types():
            if not is_struct_type(data_type):
                # Only handle user-defined structs (avoid unions and primitives)
                continue

            # Define a class for each struct
            class_def = 'class {}(object):'.format(fmt_class(data_type.name))
            self.emit(class_def)

            with self.indent():
                if data_type.doc:
                    self.emit('"""')
                    self.emit_wrapped_text(data_type.doc)
                    self.emit('"""')

                self.emit()

                # Define constructor to take each field
                args = ['self']
                for field in data_type.fields:
                    args.append(fmt_var(field.name))
                self.generate_multiline_list(args, 'def __init__', ':')

                with self.indent():
                    if data_type.fields:
                        self.emit()
                        # Body of init should assign all init vars
                        for field in data_type.fields:
                            if field.doc:
                                self.emit_wrapped_text(field.doc, '# ', '# ')
                            member_name = fmt_var(field.name)
                            self.emit('self.{0} = {0}'.format(member_name))
                    else:
                        self.emit('pass')
            self.emit()
Example #41
0
    def _generate_namespace_module(self, namespace):
        for data_type in namespace.linearize_data_types():
            if not is_struct_type(data_type):
                # Only handle user-defined structs (avoid unions and primitives)
                continue

            # Define a class for each struct
            class_def = 'class {}(object):'.format(fmt_class(data_type.name))
            self.emit(class_def)

            with self.indent():
                if data_type.doc:
                    self.emit('"""')
                    self.emit_wrapped_text(data_type.doc)
                    self.emit('"""')

                self.emit()

                # Define constructor to take each field
                args = ['self']
                for field in data_type.fields:
                    args.append(fmt_var(field.name))
                self.generate_multiline_list(args, 'def __init__', ':')

                with self.indent():
                    if data_type.fields:
                        self.emit()
                        # Body of init should assign all init vars
                        for field in data_type.fields:
                            if field.doc:
                                self.emit_wrapped_text(field.doc, '# ', '# ')
                            member_name = fmt_var(field.name)
                            self.emit('self.{0} = {0}'.format(member_name))
                    else:
                        self.emit('pass')
            self.emit()
Example #42
0
    def _generate_routes_h(self, namespace):
        """Generates header file for namespace object that has as methods
        all routes within the namespace."""
        self.emit(comment_prefix)
        self.emit_wrapped_text('Routes for the `{}` namespace'.format(
            fmt_class(namespace.name)),
                               prefix=comment_prefix)
        self.emit(comment_prefix)

        with self.block_h(fmt_routes_class(namespace.name)):
            description_str = (
                'An instance of the networking client that each '
                'route will use to submit a request.')
            self.emit_wrapped_text(description_str, prefix=comment_prefix)
            self.emit(
                fmt_property_str(prop='client',
                                 typ='{} * _Nonnull'.format(
                                     self.args.transport_client_name)))
            self.emit()

            routes_obj_args = fmt_func_args_declaration([
                ('client',
                 '{} * _Nonnull'.format(self.args.transport_client_name))
            ])

            init_signature = fmt_signature(func='init',
                                           args=routes_obj_args,
                                           return_type='nonnull instancetype')
            description_str = (
                'Initializes the `{}` namespace container object '
                'with a networking client.')
            self.emit_wrapped_text(description_str.format(
                fmt_routes_class(namespace.name)),
                                   prefix=comment_prefix)
            self.emit('{};'.format(init_signature))
            self.emit()

            style_to_request = json.loads(self.args.z__style_to_request)

            for route in namespace.routes:
                route_type = route.attrs.get('style')
                client_args = json.loads(self.args.client_args)

                if route_type in client_args.keys():
                    for args_data in client_args[route_type]:
                        task_type_key, type_data_dict = tuple(args_data)
                        task_type_name = style_to_request[task_type_key]

                        func_suffix = type_data_dict[0]
                        extra_args = [
                            tuple(type_data[:-1])
                            for type_data in type_data_dict[1]
                        ]
                        extra_docs = [(type_data[0], type_data[-1])
                                      for type_data in type_data_dict[1]]

                        if is_struct_type(route.arg_data_type
                                          ) and self._struct_has_defaults(
                                              route.arg_data_type):
                            route_args, doc_list = self._get_default_route_args(
                                namespace, route, tag=True)
                            self._generate_route_signature(
                                route, namespace, route_args, extra_args,
                                doc_list + extra_docs, task_type_name,
                                func_suffix)

                        route_args, doc_list = self._get_route_args(namespace,
                                                                    route,
                                                                    tag=True)
                        self._generate_route_signature(route, namespace,
                                                       route_args, extra_args,
                                                       doc_list + extra_docs,
                                                       task_type_name,
                                                       func_suffix)
                else:
                    task_type_name = style_to_request[route_type]
                    if is_struct_type(
                            route.arg_data_type) and self._struct_has_defaults(
                                route.arg_data_type):
                        route_args, doc_list = self._get_default_route_args(
                            namespace, route, tag=True)
                        self._generate_route_signature(route, namespace,
                                                       route_args, [],
                                                       doc_list,
                                                       task_type_name, '')

                    route_args, doc_list = self._get_route_args(namespace,
                                                                route,
                                                                tag=True)
                    self._generate_route_signature(route, namespace,
                                                   route_args, [], doc_list,
                                                   task_type_name, '')
    def _generate_union_helper(self, u):
        name = u.name
        namespace = u.namespace
        fields = u.fields
        if is_struct_type(u) and u.has_enumerated_subtypes():
            name = fmt_var(name, export=False) + 'Union'
            fields = u.get_enumerated_subtypes()

        with self.block('type %s struct' % name):
            self.emit('dropbox.Tagged')
            for field in fields:
                if is_void_type(field.data_type):
                    continue
                self._generate_field(field,
                                     union_field=True,
                                     namespace=namespace)
        self.emit()
        self.emit('// Valid tag values for %s' % fmt_var(u.name))
        with self.block('const', delim=('(', ')')):
            for field in fields:
                self.emit('%s%s = "%s"' %
                          (fmt_var(u.name), fmt_var(field.name), field.name))
        self.emit()

        num_void_fields = sum([is_void_type(f.data_type) for f in fields])
        # Simple structure, no need in UnmarshalJSON
        if len(fields) == num_void_fields:
            return

        self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
        with self.block('func (u *%s) UnmarshalJSON(body []byte) error' %
                        name):
            with self.block('type wrap struct'):
                self.emit('dropbox.Tagged')
                for field in fields:
                    if is_void_type(field.data_type) or \
                            is_primitive_type(field.data_type):
                        continue
                    self._generate_field(field,
                                         union_field=True,
                                         namespace=namespace,
                                         raw=True)
            self.emit('var w wrap')
            self.emit('var err error')
            with self.block('if err = json.Unmarshal(body, &w); err != nil'):
                self.emit('return err')
            self.emit('u.Tag = w.Tag')
            with self.block('switch u.Tag'):
                for field in fields:
                    if is_void_type(field.data_type):
                        continue
                    field_name = fmt_var(field.name)
                    with self.block('case "%s":' % field.name,
                                    delim=(None, None)):
                        if is_union_type(field.data_type):
                            self.emit(
                                'err = json.Unmarshal(w.{0}, &u.{0})'.format(
                                    field_name))
                        elif is_struct_type(field.data_type) and \
                            field.data_type.has_enumerated_subtypes():
                            self.emit(
                                "u.{0}, err = Is{1}FromJSON(body)".format(
                                    field_name, field.data_type.name))
                        else:
                            self.emit(
                                'err = json.Unmarshal(body, &u.{0})'.format(
                                    field_name))
                    with self.block("if err != nil"):
                        self.emit("return err")
            self.emit('return nil')
        self.emit()
Example #44
0
    def _generate_docstring_for_func(self,
                                     namespace,
                                     arg_data_type,
                                     result_data_type=None,
                                     error_data_type=None,
                                     overview=None,
                                     extra_request_args=None,
                                     extra_return_arg=None,
                                     footer=None):
        """
        Generates a docstring for a function or method.

        This function is versatile. It will create a docstring using all the
        data that is provided.

        :param arg_data_type: The data type describing the argument to the
            route. The data type should be a struct, and each field will be
            treated as an input parameter of the method.
        :param result_data_type: The data type of the route result.
        :param error_data_type: The data type of the route result in the case
            of an error.
        :param str overview: A description of the route that will be located
            at the top of the docstring.
        :param extra_request_args: [(field name, field type, field doc), ...]
            Describes any additional parameters for the method that aren't a
            field in arg_data_type.
        :param str extra_return_arg: Name of an additional return type that. If
            this is specified, it is assumed that the return of the function
            will be a tuple of return_data_type and extra_return-arg.
        :param str footer: Additional notes at the end of the docstring.
        """
        fields = [] if is_void_type(arg_data_type) else arg_data_type.fields
        if not fields and not overview:
            # If we don't have an overview or any input parameters, we skip the
            # docstring altogether.
            return

        self.emit('"""')
        if overview:
            self.emit_wrapped_text(overview)

        # Description of all input parameters
        if extra_request_args or fields:
            if overview:
                # Add a blank line if we had an overview
                self.emit()

            if extra_request_args:
                for name, data_type_name, doc in extra_request_args:
                    if data_type_name:
                        field_doc = ':param {} {}: {}'.format(
                            data_type_name, name, doc)
                        self.emit_wrapped_text(field_doc,
                                               subsequent_prefix='    ')
                    else:
                        self.emit_wrapped_text(':param {}: {}'.format(
                            name, doc),
                                               subsequent_prefix='    ')

            if is_struct_type(arg_data_type):
                for field in fields:
                    if field.doc:
                        if is_user_defined_type(field.data_type):
                            field_doc = ':param {}: {}'.format(
                                field.name,
                                self.process_doc(field.doc, self._docf))
                        else:
                            field_doc = ':param {} {}: {}'.format(
                                self._format_type_in_doc(
                                    namespace, field.data_type),
                                field.name,
                                self.process_doc(field.doc, self._docf),
                            )
                        self.emit_wrapped_text(field_doc,
                                               subsequent_prefix='    ')
                        if is_user_defined_type(field.data_type):
                            # It's clearer to declare the type of a composite on
                            # a separate line since it references a class in
                            # another module
                            self.emit(':type {}: {}'.format(
                                field.name,
                                self._format_type_in_doc(
                                    namespace, field.data_type),
                            ))
                    else:
                        # If the field has no docstring, then just document its
                        # type.
                        field_doc = ':type {}: {}'.format(
                            field.name,
                            self._format_type_in_doc(namespace,
                                                     field.data_type),
                        )
                        self.emit_wrapped_text(field_doc)

            elif is_union_type(arg_data_type):
                if arg_data_type.doc:
                    self.emit_wrapped_text(':param arg: {}'.format(
                        self.process_doc(arg_data_type.doc, self._docf)),
                                           subsequent_prefix='    ')
                self.emit(':type arg: {}'.format(
                    self._format_type_in_doc(namespace, arg_data_type)))

        if overview and not (extra_request_args or fields):
            # Only output an empty line if we had an overview and haven't
            # started a section on declaring types.
            self.emit()

        if extra_return_arg:
            # Special case where the function returns a tuple. The first
            # element is the JSON response. The second element is the
            # the extra_return_arg param.
            args = []
            if is_void_type(result_data_type):
                args.append('None')
            else:
                rtype = self._format_type_in_doc(namespace, result_data_type)
                args.append(rtype)
            args.append(extra_return_arg)
            self.generate_multiline_list(args, ':rtype: ')
        else:
            if is_void_type(result_data_type):
                self.emit(':rtype: None')
            else:
                rtype = self._format_type_in_doc(namespace, result_data_type)
                self.emit(':rtype: {}'.format(rtype))

        if not is_void_type(error_data_type) and error_data_type.fields:
            self.emit(':raises: :class:`dropbox.exceptions.ApiError`')
            self.emit()
            # To provide more clarity to a dev who reads the docstring, state
            # the error class that will be returned in the reason field of an
            # ApiError object.
            self.emit('If this raises, ApiError.reason is of type:')
            with self.indent():
                self.emit(self._format_type_in_doc(namespace, error_data_type))

        if footer:
            self.emit()
            self.emit_wrapped_text(footer)
        self.emit('"""')
def _needs_base_type(data_type):
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        return True
    if is_list_type(data_type):
        return _needs_base_type(data_type.data_type)
    return False
Example #46
0
    def _generate_route_helper(self, namespace, route, download_to_file=False):
        """Generate a Python method that corresponds to a route.

        :param namespace: Namespace that the route belongs to.
        :param bool download_to_file: Whether a special version of the route
            that downloads the response body to a file should be generated.
            This can only be used for download-style routes.
        """
        arg_data_type = route.arg_data_type
        result_data_type = route.result_data_type

        request_binary_body = route.attrs.get('style') == 'upload'
        response_binary_body = route.attrs.get('style') == 'download'

        if download_to_file:
            assert response_binary_body, 'download_to_file can only be set ' \
                'for download-style routes.'
            self._generate_route_method_decl(namespace,
                                             route,
                                             arg_data_type,
                                             request_binary_body,
                                             method_name_suffix='_to_file',
                                             extra_args=['download_path'])
        else:
            self._generate_route_method_decl(namespace, route, arg_data_type,
                                             request_binary_body)

        with self.indent():
            extra_request_args = None
            extra_return_arg = None
            footer = None
            if request_binary_body:
                extra_request_args = [('f', 'bytes', 'Contents to upload.')]
            elif download_to_file:
                extra_request_args = [('download_path', 'str',
                                       'Path on local machine to save file.')]
            if response_binary_body:
                extra_return_arg = ':class:`requests.models.Response`'
                if not download_to_file:
                    footer = DOCSTRING_CLOSE_RESPONSE

            if route.doc:
                func_docstring = self.process_doc(route.doc, self._docf)
            else:
                func_docstring = None

            self._generate_docstring_for_func(
                namespace,
                arg_data_type,
                result_data_type,
                route.error_data_type,
                overview=func_docstring,
                extra_request_args=extra_request_args,
                extra_return_arg=extra_return_arg,
                footer=footer,
            )

            self._maybe_generate_deprecation_warning(route)

            # Code to instantiate a class for the request data type
            if is_void_type(arg_data_type):
                self.emit('arg = None')
            elif is_struct_type(arg_data_type):
                self.generate_multiline_list(
                    [f.name for f in arg_data_type.all_fields],
                    before='arg = {}.{}'.format(arg_data_type.namespace.name,
                                                fmt_class(arg_data_type.name)),
                )
            elif not is_union_type(arg_data_type):
                raise AssertionError('Unhandled request type %r' %
                                     arg_data_type)

            # Code to make the request
            args = [
                '{}.{}'.format(namespace.name, fmt_var(route.name)),
                "'{}'".format(namespace.name), 'arg'
            ]
            if request_binary_body:
                args.append('f')
            else:
                args.append('None')
            self.generate_multiline_list(args,
                                         'r = self.request',
                                         compact=False)

            if download_to_file:
                self.emit('self._save_body_to_file(download_path, r[1])')
                if is_void_type(result_data_type):
                    self.emit('return None')
                else:
                    self.emit('return r[0]')
            else:
                if is_void_type(result_data_type):
                    self.emit('return None')
                else:
                    self.emit('return r')
        self.emit()
Example #47
0
    def _generate_namespace_tests(self, namespace):
        ns_name = fmt_public_name(namespace.name)

        self.emit()
        self.emit(
            '/// Serialization tests for the {} namespace.'.format(ns_name))
        self.emit()
        self.emit()
        for data_type in namespace.linearize_data_types():
            class_name = fmt_public_name(data_type.name)
            if is_user_defined_type(data_type):
                examples = data_type.get_examples()
                for example_type in examples:
                    test_name = 'testSerialize{}{}{}'.format(
                        ns_name, class_name,
                        fmt_camel_upper(example_type, reserved=False))
                    with self.block_func(func=test_name, args=[]):
                        self.emit('/// Data from the "{}" example'.format(
                            example_type))
                        example_data = examples[example_type].value
                        result_args = []

                        for field in data_type.all_fields:
                            if field.name in example_data:
                                result_args += self._get_example_data(
                                    example_data[field.name], field)
                            else:
                                if not is_void_type(field.data_type):
                                    result_args.append(
                                        (fmt_var(field.name),
                                         self._fmt_default(field.data_type)))

                        args_str = fmt_func_args(result_args)

                        if '\n' not in args_str:
                            if is_struct_type(
                                    data_type
                            ) and data_type.has_enumerated_subtypes():
                                for tags, subtype in data_type.get_all_subtypes_with_tags(
                                ):
                                    assert len(tags) == 1, tags
                                    tag = tags[0]
                                    if tag == example_data['.tag']:
                                        self.emit('{} *obj = {};'.format(
                                            fmt_class_prefix(subtype),
                                            self._get_example_data(
                                                example_data, subtype)))
                                        self.emit(
                                            'NSData *serializedData = [DropboxTransportClient jsonDataWithDictionary:[{} serialize:obj]];'
                                            .format(fmt_class_prefix(subtype)))
                                        self.emit(
                                            'id jsonObj = [NSJSONSerialization JSONObjectWithData:serializedData options:NSJSONReadingMutableContainers error:nil];'
                                        )
                                        self.emit(
                                            '{} *outputObj = [{} deserialize:jsonObj];'
                                            .format(fmt_class_prefix(subtype),
                                                    fmt_class_prefix(subtype)))
                                        self.emit(
                                            '[self checkError:obj outputObj:outputObj];'
                                        )
                            else:
                                self.emit('{} *obj = {};'.format(
                                    fmt_class_prefix(data_type),
                                    fmt_func_call(
                                        fmt_alloc_call(
                                            fmt_class_prefix(data_type)),
                                        self._cstor_name_from_fields_names(
                                            result_args), args_str)))
                                self.emit(
                                    'NSData *serializedData = [DropboxTransportClient jsonDataWithDictionary:[{} serialize:obj]];'
                                    .format(fmt_class_prefix(data_type)))
                                self.emit(
                                    'id jsonObj = [NSJSONSerialization JSONObjectWithData:serializedData options:NSJSONReadingMutableContainers error:nil];'
                                )
                                self.emit(
                                    '{} *outputObj = [{} deserialize:jsonObj];'
                                    .format(fmt_class_prefix(data_type),
                                            fmt_class_prefix(data_type)))
                                self.emit(
                                    '[self checkError:obj outputObj:outputObj];'
                                )
                    self.emit()
Example #48
0
    def _generate_routes_h(self, namespace):
        """Generates header file for namespace object that has as methods
        all routes within the namespace."""
        self.emit(comment_prefix)
        self.emit_wrapped_text(
            'Routes for the `{}` namespace'.format(fmt_class(namespace.name)),
            prefix=comment_prefix)
        self.emit(comment_prefix)

        self.emit()
        self.emit('NS_ASSUME_NONNULL_BEGIN')
        self.emit()

        with self.block_h(
                fmt_routes_class(namespace.name, self.args.auth_type)):
            description_str = (
                'An instance of the networking client that each '
                'route will use to submit a request.')
            self.emit_wrapped_text(description_str, prefix=comment_prefix)
            self.emit(
                fmt_property_str(
                    prop='client',
                    typ='id<{}>'.format(
                        self.args.transport_client_name)))
            self.emit()

            routes_obj_args = fmt_func_args_declaration(
                [('client',
                  'id<{}>'.format(self.args.transport_client_name))])

            init_signature = fmt_signature(
                func='init',
                args=routes_obj_args,
                return_type='instancetype')
            description_str = (
                'Initializes the `{}` namespace container object '
                'with a networking client.')
            self.emit_wrapped_text(
                description_str.format(
                    fmt_routes_class(namespace.name, self.args.auth_type)),
                prefix=comment_prefix)
            self.emit('{};'.format(init_signature))
            self.emit()

            style_to_request = json.loads(self.args.z__style_to_request)

            for route in namespace.routes:
                if (route.attrs.get('auth') != self.args.auth_type
                        and route.attrs.get('auth') != 'noauth'):
                    continue

                route_type = route.attrs.get('style')
                client_args = json.loads(self.args.client_args)

                if route_type in client_args.keys():
                    for args_data in client_args[route_type]:
                        task_type_key, type_data_dict = tuple(args_data)
                        task_type_name = style_to_request[task_type_key]

                        func_suffix = type_data_dict[0]
                        extra_args = [
                            tuple(type_data[:-1])
                            for type_data in type_data_dict[1]
                        ]
                        extra_docs = [(type_data[0], type_data[-1])
                                      for type_data in type_data_dict[1]]

                        if (is_struct_type(route.arg_data_type) and
                                self._struct_has_defaults(route.arg_data_type)):
                            route_args, doc_list = self._get_default_route_args(
                                namespace, route, tag=True)
                            self._generate_route_signature(
                                route, namespace, route_args, extra_args,
                                doc_list + extra_docs, task_type_name,
                                func_suffix)

                        route_args, doc_list = self._get_route_args(
                            namespace, route, tag=True)
                        self._generate_route_signature(
                            route, namespace, route_args, extra_args,
                            doc_list + extra_docs, task_type_name, func_suffix)
                else:
                    task_type_name = style_to_request[route_type]
                    if (is_struct_type(route.arg_data_type) and
                            self._struct_has_defaults(route.arg_data_type)):
                        route_args, doc_list = self._get_default_route_args(
                            namespace, route, tag=True)
                        self._generate_route_signature(
                            route, namespace, route_args, [], doc_list,
                            task_type_name, '')

                    route_args, doc_list = self._get_route_args(
                        namespace, route, tag=True)
                    self._generate_route_signature(route, namespace,
                                                   route_args, [], doc_list,
                                                   task_type_name, '')
        self.emit()
        self.emit('NS_ASSUME_NONNULL_END')
        self.emit()
def _needs_base_type(data_type):
    if is_struct_type(data_type) and data_type.has_enumerated_subtypes():
        return True
    if is_list_type(data_type):
        return _needs_base_type(data_type.data_type)
    return False
Example #50
0
 def _generate_type(self, data_type, extra_parameters):
     if is_struct_type(data_type):
         self._generate_struct(data_type, extra_parameters)
     elif is_union_type(data_type):
         self._generate_union(data_type)
Example #51
0
    def _generate_route_helper(self, namespace, route, download_to_file=False):
        """Generate a Python method that corresponds to a route.

        :param namespace: Namespace that the route belongs to.
        :param bool download_to_file: Whether a special version of the route
            that downloads the response body to a file should be generated.
            This can only be used for download-style routes.
        """
        arg_data_type = route.arg_data_type
        result_data_type = route.result_data_type

        request_binary_body = route.attrs.get('style') == 'upload'
        response_binary_body = route.attrs.get('style') == 'download'

        if download_to_file:
            assert response_binary_body, 'download_to_file can only be set ' \
                'for download-style routes.'
            self._generate_route_method_decl(namespace,
                                             route,
                                             arg_data_type,
                                             request_binary_body,
                                             method_name_suffix='_to_file',
                                             extra_args=['download_path'])
        else:
            self._generate_route_method_decl(namespace,
                                             route,
                                             arg_data_type,
                                             request_binary_body)

        with self.indent():
            extra_request_args = None
            extra_return_arg = None
            footer = None
            if request_binary_body:
                extra_request_args = [('f',
                                       'bytes',
                                       'Contents to upload.')]
            elif download_to_file:
                extra_request_args = [('download_path',
                                       'str',
                                       'Path on local machine to save file.')]
            if response_binary_body:
                extra_return_arg = ':class:`requests.models.Response`'
                if not download_to_file:
                    footer = DOCSTRING_CLOSE_RESPONSE

            if route.doc:
                func_docstring = self.process_doc(route.doc, self._docf)
            else:
                func_docstring = None

            self._generate_docstring_for_func(
                namespace,
                arg_data_type,
                result_data_type,
                route.error_data_type,
                overview=func_docstring,
                extra_request_args=extra_request_args,
                extra_return_arg=extra_return_arg,
                footer=footer,
            )

            self._maybe_generate_deprecation_warning(route)

            # Code to instantiate a class for the request data type
            if is_void_type(arg_data_type):
                self.emit('arg = None')
            elif is_struct_type(arg_data_type):
                self.generate_multiline_list(
                    [f.name for f in arg_data_type.all_fields],
                    before='arg = {}.{}'.format(
                        arg_data_type.namespace.name,
                        fmt_class(arg_data_type.name)),
                )
            elif not is_union_type(arg_data_type):
                raise AssertionError('Unhandled request type %r' %
                                     arg_data_type)

            # Code to make the request
            args = [
                '{}.{}'.format(namespace.name, fmt_var(route.name)),
                "'{}'".format(namespace.name),
                'arg']
            if request_binary_body:
                args.append('f')
            else:
                args.append('None')
            self.generate_multiline_list(args, 'r = self.request', compact=False)

            if download_to_file:
                self.emit('self._save_body_to_file(download_path, r[1])')
                if is_void_type(result_data_type):
                    self.emit('return None')
                else:
                    self.emit('return r[0]')
            else:
                if is_void_type(result_data_type):
                    self.emit('return None')
                else:
                    self.emit('return r')
        self.emit()
Example #52
0
    def _emit_route(self,
                    namespace,
                    route,
                    req_obj_name,
                    extra_args=None,
                    extra_docs=None):
        arg_list, doc_list = self._get_route_args(namespace, route)
        extra_args = extra_args or []
        extra_docs = extra_docs or []

        arg_type = fmt_type(route.arg_data_type)
        func_name = fmt_func(route.name)

        if route.doc:
            route_doc = self.process_doc(route.doc, self._docf)
        else:
            route_doc = 'The {} route'.format(func_name)
        self.emit_wrapped_text(route_doc, prefix='/// ', width=120)
        self.emit('///')

        for name, doc in doc_list + extra_docs:
            param_doc = '- parameter {}: {}'.format(
                name, doc if doc is not None else undocumented)
            self.emit_wrapped_text(param_doc, prefix='/// ', width=120)
        self.emit('///')
        output = (
            ' - returns: Through the response callback, the caller will ' +
            'receive a `{}` object on success or a `{}` object on failure.')
        output = output.format(fmt_type(route.result_data_type),
                               fmt_type(route.error_data_type))
        self.emit_wrapped_text(output, prefix='/// ', width=120)

        func_args = [
            ('route', '{}.{}'.format(fmt_class(namespace.name), func_name)),
        ]
        client_args = []
        return_args = [('route', 'route')]

        for name, value, typ in extra_args:
            arg_list.append((name, typ))
            func_args.append((name, value))
            client_args.append((name, value))

        rtype = fmt_serial_type(route.result_data_type)
        etype = fmt_serial_type(route.error_data_type)

        self._maybe_generate_deprecation_warning(route)

        with self.function_block(
                '@discardableResult open func {}'.format(func_name),
                args=self._func_args(arg_list, force_first=False),
                return_type='{}<{}, {}>'.format(req_obj_name, rtype, etype)):
            self.emit('let route = {}.{}'.format(fmt_class(namespace.name),
                                                 func_name))
            if is_struct_type(route.arg_data_type):
                args = [
                    (name, name)
                    for name, _ in self._struct_init_args(route.arg_data_type)
                ]
                func_args += [
                    ('serverArgs', '{}({})'.format(arg_type,
                                                   self._func_args(args)))
                ]
                self.emit('let serverArgs = {}({})'.format(
                    arg_type, self._func_args(args)))
            elif is_union_type(route.arg_data_type):
                self.emit('let serverArgs = {}'.format(
                    fmt_var(route.arg_data_type.name)))

            if not is_void_type(route.arg_data_type):
                return_args += [('serverArgs', 'serverArgs')]

            return_args += client_args

            self.emit('return client.request({})'.format(
                self._func_args(return_args, not_init=True)))
        self.emit()
Example #53
0
    def _generate_docstring_for_func(self, namespace, arg_data_type,
                                     result_data_type=None, error_data_type=None,
                                     overview=None, extra_request_args=None,
                                     extra_return_arg=None, footer=None):
        """
        Generates a docstring for a function or method.

        This function is versatile. It will create a docstring using all the
        data that is provided.

        :param arg_data_type: The data type describing the argument to the
            route. The data type should be a struct, and each field will be
            treated as an input parameter of the method.
        :param result_data_type: The data type of the route result.
        :param error_data_type: The data type of the route result in the case
            of an error.
        :param str overview: A description of the route that will be located
            at the top of the docstring.
        :param extra_request_args: [(field name, field type, field doc), ...]
            Describes any additional parameters for the method that aren't a
            field in arg_data_type.
        :param str extra_return_arg: Name of an additional return type that. If
            this is specified, it is assumed that the return of the function
            will be a tuple of return_data_type and extra_return-arg.
        :param str footer: Additional notes at the end of the docstring.
        """
        fields = [] if is_void_type(arg_data_type) else arg_data_type.fields
        if not fields and not overview:
            # If we don't have an overview or any input parameters, we skip the
            # docstring altogether.
            return

        self.emit('"""')
        if overview:
            self.emit_wrapped_text(overview)

        # Description of all input parameters
        if extra_request_args or fields:
            if overview:
                # Add a blank line if we had an overview
                self.emit()

            if extra_request_args:
                for name, data_type_name, doc in extra_request_args:
                    if data_type_name:
                        field_doc = ':param {} {}: {}'.format(data_type_name,
                                                              name, doc)
                        self.emit_wrapped_text(field_doc,
                                               subsequent_prefix='    ')
                    else:
                        self.emit_wrapped_text(
                            ':param {}: {}'.format(name, doc),
                            subsequent_prefix='    ')

            if is_struct_type(arg_data_type):
                for field in fields:
                    if field.doc:
                        if is_user_defined_type(field.data_type):
                            field_doc = ':param {}: {}'.format(
                                field.name, self.process_doc(field.doc, self._docf))
                        else:
                            field_doc = ':param {} {}: {}'.format(
                                self._format_type_in_doc(namespace, field.data_type),
                                field.name,
                                self.process_doc(field.doc, self._docf),
                            )
                        self.emit_wrapped_text(
                            field_doc, subsequent_prefix='    ')
                        if is_user_defined_type(field.data_type):
                            # It's clearer to declare the type of a composite on
                            # a separate line since it references a class in
                            # another module
                            self.emit(':type {}: {}'.format(
                                field.name,
                                self._format_type_in_doc(namespace, field.data_type),
                            ))
                    else:
                        # If the field has no docstring, then just document its
                        # type.
                        field_doc = ':type {}: {}'.format(
                            field.name,
                            self._format_type_in_doc(namespace, field.data_type),
                        )
                        self.emit_wrapped_text(field_doc)

            elif is_union_type(arg_data_type):
                if arg_data_type.doc:
                    self.emit_wrapped_text(':param arg: {}'.format(
                        self.process_doc(arg_data_type.doc, self._docf)),
                        subsequent_prefix='    ')
                self.emit(':type arg: {}'.format(
                    self._format_type_in_doc(namespace, arg_data_type)))

        if overview and not (extra_request_args or fields):
            # Only output an empty line if we had an overview and haven't
            # started a section on declaring types.
            self.emit()

        if extra_return_arg:
            # Special case where the function returns a tuple. The first
            # element is the JSON response. The second element is the
            # the extra_return_arg param.
            args = []
            if is_void_type(result_data_type):
                args.append('None')
            else:
                rtype = self._format_type_in_doc(namespace,
                                                 result_data_type)
                args.append(rtype)
            args.append(extra_return_arg)
            self.generate_multiline_list(args, ':rtype: ')
        else:
            if is_void_type(result_data_type):
                self.emit(':rtype: None')
            else:
                rtype = self._format_type_in_doc(namespace, result_data_type)
                self.emit(':rtype: {}'.format(rtype))

        if not is_void_type(error_data_type) and error_data_type.fields:
            self.emit(':raises: :class:`dropbox.exceptions.ApiError`')
            self.emit()
            # To provide more clarity to a dev who reads the docstring, state
            # the error class that will be returned in the reason field of an
            # ApiError object.
            self.emit('If this raises, ApiError.reason is of type:')
            with self.indent():
                self.emit(self._format_type_in_doc(namespace, error_data_type))

        if footer:
            self.emit()
            self.emit_wrapped_text(footer)
        self.emit('"""')