Ejemplo n.º 1
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")')
Ejemplo n.º 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_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")')
Ejemplo n.º 3
0
    def _generate_route_signature(
            self,
            route,
            namespace,  # pylint: disable=unused-argument
            route_args,
            extra_args,
            doc_list,
            task_type_name,
            func_suffix):
        """Generates route method signature for the given route."""
        for name, _, typ in extra_args:
            route_args.append((name, typ))

        deprecated = 'DEPRECATED: ' if route.deprecated else ''

        func_name = '{}{}'.format(fmt_var(route.name), func_suffix)

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

        for name, doc in doc_list:
            self.emit_wrapped_text('@param {} {}'.format(
                name, doc if doc else undocumented),
                                   prefix=comment_prefix,
                                   width=120)
        self.emit(comment_prefix)
        output = (
            '@return 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, tag=False, no_ptr=True),
            fmt_type(route.error_data_type, tag=False, no_ptr=True))
        self.emit_wrapped_text(output, prefix=comment_prefix, width=120)
        self.emit(comment_prefix)

        result_type_str = fmt_type(route.result_data_type) if not is_void_type(
            route.result_data_type) else 'DBNilObject *'
        error_type_str = fmt_type(route.error_data_type) if not is_void_type(
            route.error_data_type) else 'DBNilObject *'

        return_type = '{}<{}, {}> *'.format(task_type_name, result_type_str,
                                            error_type_str)

        deprecated = self._get_deprecation_warning(route)
        route_signature = fmt_signature(
            func=func_name,
            args=fmt_func_args_declaration(route_args),
            return_type='{}'.format(return_type))
        self.emit('{}{};'.format(route_signature, deprecated))
        self.emit()
Ejemplo n.º 4
0
    def _generate_route_signature(
            self,
            route,
            namespace,  # pylint: disable=unused-argument
            route_args,
            extra_args,
            doc_list,
            task_type_name,
            func_suffix):
        """Generates route method signature for the given route."""
        for name, _, typ in extra_args:
            route_args.append((name, typ))

        deprecated = 'DEPRECATED: ' if route.deprecated else ''

        func_name = '{}{}'.format(fmt_var(route.name), func_suffix)

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

        for name, doc in doc_list:
            self.emit_wrapped_text(
                '@param {} {}'.format(name, doc if doc else undocumented),
                prefix=comment_prefix,
                width=120)
        self.emit(comment_prefix)
        output = (
            '@return 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, tag=False, no_ptr=True),
            fmt_type(route.error_data_type, tag=False, no_ptr=True))
        self.emit_wrapped_text(output, prefix=comment_prefix, width=120)
        self.emit(comment_prefix)

        result_type_str = fmt_type(route.result_data_type) if not is_void_type(
            route.result_data_type) else 'DBNilObject *'
        error_type_str = fmt_type(route.error_data_type) if not is_void_type(
            route.error_data_type) else 'DBNilObject *'

        return_type = '{}<{}, {}> *'.format(task_type_name, result_type_str,
                                            error_type_str)

        deprecated = self._get_deprecation_warning(route)
        route_signature = fmt_signature(
            func=func_name,
            args=fmt_func_args_declaration(route_args),
            return_type='{}'.format(return_type))
        self.emit('{}{};'.format(route_signature, deprecated))
        self.emit()
Ejemplo n.º 5
0
    def _generate_request(self, namespace, route):
        out = self.emit
        auth = route.attrs.get('auth', '')
        host = route.attrs.get('host', 'api')
        style = route.attrs.get('style', 'rpc')

        body = 'nil'
        if not is_void_type(route.arg_data_type):
            out('dbx.Config.LogDebug("arg: %v", arg)')

            out('b, err := json.Marshal(arg)')
            with self.block('if err != nil'):
                out('return')
            out()
            if host != 'content':
                body = 'bytes.NewReader(b)'
        if style == 'upload':
            body = 'content'

        headers = {}
        if not is_void_type(route.arg_data_type):
            if host == 'content' or style in ['upload', 'download']:
                headers["Dropbox-API-Arg"] = "dropbox.HTTPHeaderSafeJSON(b)"
            else:
                headers["Content-Type"] = '"application/json"'
        if style == 'upload':
            headers["Content-Type"] = '"application/octet-stream"'

        out('headers := map[string]string{')
        for k, v in sorted(headers.items()):
            out('\t"{}": {},'.format(k, v))
        out('}')
        if fmt_var(route.name) == "Download":
            out('for k, v := range arg.ExtraHeaders { headers[k] = v }')
        if auth != 'noauth' and auth != 'team':
            with self.block('if dbx.Config.AsMemberID != ""'):
                out('headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID'
                    )
        out()

        fn = route.name
        if route.version != 1:
            fn += '_v%d' % route.version
        authed = 'false' if auth == 'noauth' else 'true'
        out('req, err := (*dropbox.Context)(dbx).NewRequest("{}", "{}", {}, "{}", "{}", headers, {})'
            .format(host, style, authed, namespace.name, fn, body))
        with self.block('if err != nil'):
            out('return')

        out('dbx.Config.LogInfo("req: %v", req)')

        out()
Ejemplo n.º 6
0
    def _generate_union_type(self, union_type, indent_spaces):
        """
        Generates a TypeScript interface for a stone union.
        """
        # Emit an interface for each variant. TypeScript 2.0 supports these tagged unions.
        # https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#tagged-union-types
        parent_type = union_type.parent_type
        namespace = union_type.namespace
        union_type_name = fmt_type_name(union_type, namespace)
        variant_type_names = []
        if parent_type:
            variant_type_names.append(fmt_type_name(parent_type, namespace))
        for variant in union_type.fields:
            if variant.doc:
                self._emit_tsdoc_header(variant.doc)
            variant_name = '%s%s' % (union_type_name, fmt_pascal(variant.name))
            variant_type_names.append(variant_name)
            self.emit('interface %s {' % variant_name)
            with self.indent(dent=indent_spaces):
                # Since field contains non-alphanumeric character, we need to enclose
                # it in quotation marks.
                self.emit("'.tag': '%s';" % variant.name)
                if is_void_type(variant.data_type) is False:
                    self.emit("%s: %s;" % (variant.name, fmt_type(variant.data_type, namespace)))
            self.emit('}')
            self.emit()

        if union_type.doc:
            self._emit_tsdoc_header(union_type.doc)
        self.emit('type %s = %s;' % (union_type_name, ' | '.join(variant_type_names)))
        self.emit()
Ejemplo n.º 7
0
    def _emit_closed_union_test(self, ns, typ):
        ns_name = self.namespace_name(ns)
        type_name = self.struct_name(typ)
        with self._test_fn("ClosedUnion_" + type_name):
            self.emit(
                u'// This test ensures that an exhaustive match compiles.')
            self.emit(u'let x: Option<::dropbox_sdk::{}::{}> = None;'.format(
                ns_name, self.enum_name(typ)))
            self.emit(u'match x {')
            with self.indent():
                var_exps = []
                for variant in self.get_enum_variants(typ):
                    v_name = self.enum_variant_name(variant)
                    var_exp = u'::dropbox_sdk::{}::{}::{}'.format(
                        ns_name, type_name, v_name)
                    if not ir.is_void_type(variant.data_type):
                        var_exp += u'(_)'
                    var_exps += [var_exp]

                self.generate_multiline_list(
                    [u'None'] + [u'Some({})'.format(exp) for exp in var_exps],
                    sep=' | ',
                    skip_last_sep=True,
                    delim=('', ''),
                    after=' => ()')
            self.emit(u'}')
        self.emit()
Ejemplo n.º 8
0
    def _generate_union_class_variant_creators(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """
        Generate the following section in the 'union Shape' example:
        @classmethod
        def circle(cls, val: float) -> Shape: ...
        """
        union_type = class_name_for_data_type(data_type)

        for field in data_type.fields:
            if not is_void_type(field.data_type):
                field_name_reserved_check = fmt_func(field.name,
                                                     check_reserved=True)
                val_type = self.map_stone_type_to_pep484_type(
                    ns, field.data_type)

                self.emit('@classmethod')
                self.emit(
                    'def {field_name}(cls, val: {val_type}) -> {union_type}: ...'
                    .format(
                        field_name=field_name_reserved_check,
                        val_type=val_type,
                        union_type=union_type,
                    ))
                self.emit()
Ejemplo n.º 9
0
 def _generate_union_class_variant_creators(self, ns, data_type):
     """
     Each non-symbol, non-any variant has a corresponding class method that
     can be used to construct a union with that variant selected.
     """
     for field in data_type.fields:
         if not is_void_type(field.data_type):
             field_name = fmt_func(field.name)
             field_name_reserved_check = fmt_func(field.name, True)
             if is_nullable_type(field.data_type):
                 field_dt = field.data_type.data_type
             else:
                 field_dt = field.data_type
             self.emit('@classmethod')
             self.emit('def {}(cls, val):'.format(field_name_reserved_check))
             with self.indent():
                 self.emit('"""')
                 self.emit_wrapped_text(
                     'Create an instance of this class set to the ``%s`` '
                     'tag with value ``val``.' % field_name)
                 self.emit()
                 self.emit(':param {} val:'.format(
                     self._python_type_mapping(ns, field_dt)))
                 self.emit(':rtype: {}'.format(
                     fmt_class(data_type.name)))
                 self.emit('"""')
                 self.emit("return cls('{}', val)".format(field_name))
             self.emit()
Ejemplo n.º 10
0
 def _generate_union(self, union_type):
     """
     Emits a JSDoc @typedef for a union type.
     """
     union_name = fmt_type_name(union_type)
     self._emit_jsdoc_header(union_type.doc)
     self.emit(' * @typedef {Object} %s' % union_name)
     variant_types = []
     for variant in union_type.all_fields:
         variant_types.append("'%s'" % variant.name)
         variant_data_type, _, _ = unwrap(variant.data_type)
         # Don't emit fields for void types.
         if not is_void_type(variant_data_type):
             variant_doc = ' - Available if .tag is %s.' % variant.name
             if variant.doc:
                 variant_doc += ' ' + variant.doc
             self.emit_wrapped_text(
                 '@property {%s} [%s]%s' % (
                     fmt_type(variant_data_type),
                     variant.name,
                     variant_doc,
                 ),
                 prefix=' * ',
             )
     jsdoc_tag_union = fmt_jsdoc_union(variant_types)
     self.emit(' * @property {%s} .tag - Tag identifying the union variant.' % jsdoc_tag_union)
     self.emit(' */')
Ejemplo n.º 11
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
Ejemplo n.º 12
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, _ = unwrap_nullable(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
Ejemplo n.º 13
0
 def _generate_union_class_variant_creators(self, ns, data_type):
     """
     Each non-symbol, non-any variant has a corresponding class method that
     can be used to construct a union with that variant selected.
     """
     for field in data_type.fields:
         if not is_void_type(field.data_type):
             field_name = fmt_func(field.name)
             field_name_reserved_check = fmt_func(field.name, True)
             if is_nullable_type(field.data_type):
                 field_dt = field.data_type.data_type
             else:
                 field_dt = field.data_type
             self.emit('@classmethod')
             self.emit(
                 'def {}(cls, val):'.format(field_name_reserved_check))
             with self.indent():
                 self.emit('"""')
                 self.emit_wrapped_text(
                     'Create an instance of this class set to the ``%s`` '
                     'tag with value ``val``.' % field_name)
                 self.emit()
                 self.emit(':param {} val:'.format(
                     self._python_type_mapping(ns, field_dt)))
                 self.emit(':rtype: {}'.format(fmt_class(data_type.name)))
                 self.emit('"""')
                 self.emit("return cls('{}', val)".format(field_name))
             self.emit()
Ejemplo n.º 14
0
 def _generate_union(self, union_type):
     """
     Emits a JSDoc @typedef for a union type.
     """
     union_name = fmt_type_name(union_type)
     self._emit_jsdoc_header(union_type.doc)
     self.emit(' * @typedef {Object} %s' % union_name)
     variant_types = []
     for variant in union_type.all_fields:
         variant_types.append("'%s'" % variant.name)
         variant_data_type, _, _ = unwrap(variant.data_type)
         # Don't emit fields for void types.
         if not is_void_type(variant_data_type):
             variant_doc = ' - Available if .tag is %s.' % variant.name
             if variant.doc:
                 variant_doc += ' ' + variant.doc
             self.emit_wrapped_text(
                 '@property {%s} [%s]%s' % (
                     fmt_type(variant_data_type),
                     variant.name,
                     variant_doc,
                 ),
                 prefix=' * ',
             )
     jsdoc_tag_union = fmt_jsdoc_union(variant_types)
     self.emit(
         ' * @property {%s} .tag - Tag identifying the union variant.' %
         jsdoc_tag_union)
     self.emit(' */')
Ejemplo n.º 15
0
 def _format_type_in_doc(self, namespace, data_type):
     """
     Returns a string that can be recognized by Sphinx as a type reference
     in a docstring.
     """
     if is_void_type(data_type):
         return 'None'
     elif is_user_defined_type(data_type):
         return ':class:`{}.{}.{}`'.format(
             self.args.types_package, namespace.name, fmt_type(data_type))
     elif is_nullable_type(data_type):
         return 'Nullable[{}]'.format(
             self._format_type_in_doc(namespace, data_type.data_type),
         )
     elif is_list_type(data_type):
         return 'List[{}]'.format(
             self._format_type_in_doc(namespace, data_type.data_type),
         )
     elif is_map_type(data_type):
         return 'Map[{}, {}]'.format(
             self._format_type_in_doc(namespace, data_type.key_data_type),
             self._format_type_in_doc(namespace, data_type.value_data_type),
         )
     else:
         return fmt_type(data_type)
Ejemplo n.º 16
0
        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)
Ejemplo n.º 17
0
        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, _ = unwrap_nullable(data_type.data_type)

            if not is_void_type(data_type) and is_user_defined_type(data_type):
                result.append(data_type)
Ejemplo n.º 18
0
    def _generate_route_signature(self, namespace, route):
        req = fmt_type(route.arg_data_type, namespace)
        res = fmt_type(route.result_data_type, namespace, use_interface=True)
        fn = fmt_var(route.name)
        style = route.attrs.get('style', 'rpc')

        arg = '' if is_void_type(route.arg_data_type) else 'arg {req}'
        ret = '(err error)' if is_void_type(route.result_data_type) else \
            '(res {res}, err error)'
        signature = '{fn}(' + arg + ') ' + ret
        if style == 'download':
            signature = '{fn}(' + arg + \
                ') (res {res}, content io.ReadCloser, err error)'
        elif style == 'upload':
            signature = '{fn}(' + arg + ', content io.Reader) ' + ret
            if is_void_type(route.arg_data_type):
                signature = '{fn}(content io.Reader) ' + ret
        return signature.format(fn=fn, req=req, res=res)
Ejemplo n.º 19
0
def generate_validator_constructor(ns, data_type):
    """
    Given a Stone data type, returns a string that can be used to construct
    the appropriate validation object in Python.
    """
    dt, nullable_dt = unwrap_nullable(data_type)
    if is_list_type(dt):
        v = generate_func_call(
            'bv.List',
            args=[generate_validator_constructor(ns, dt.data_type)],
            kwargs=[('min_items', dt.min_items), ('max_items', dt.max_items)],
        )
    elif is_map_type(dt):
        v = generate_func_call(
            'bv.Map',
            args=[
                generate_validator_constructor(ns, dt.key_data_type),
                generate_validator_constructor(ns, dt.value_data_type),
            ])
    elif is_numeric_type(dt):
        v = generate_func_call(
            'bv.{}'.format(dt.name),
            kwargs=[('min_value', dt.min_value), ('max_value', dt.max_value)],
        )
    elif is_string_type(dt):
        pattern = None
        if dt.pattern is not None:
            pattern = repr(dt.pattern)
        v = generate_func_call(
            'bv.String',
            kwargs=[('min_length', dt.min_length),
                    ('max_length', dt.max_length), ('pattern', pattern)],
        )
    elif is_timestamp_type(dt):
        v = generate_func_call(
            'bv.Timestamp',
            args=[repr(dt.format)],
        )
    elif is_user_defined_type(dt):
        v = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            v = '{}.{}'.format(dt.namespace.name, v)
    elif is_alias(dt):
        # Assume that the alias has already been declared elsewhere.
        name = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            name = '{}.{}'.format(dt.namespace.name, name)
        v = name
    elif is_boolean_type(dt) or is_bytes_type(dt) or is_void_type(dt):
        v = generate_func_call('bv.{}'.format(dt.name))
    else:
        raise AssertionError('Unsupported data type: %r' % dt)

    if nullable_dt:
        return generate_func_call('bv.Nullable', args=[v])
    else:
        return v
Ejemplo n.º 20
0
    def _generate_union_type(self, union_type, indent_spaces):
        """
        Generates a TypeScript interface for a stone union.
        """
        # Emit an interface for each variant. TypeScript 2.0 supports these tagged unions.
        # https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#tagged-union-types
        parent_type = union_type.parent_type
        namespace = union_type.namespace
        union_type_name = fmt_type_name(union_type, namespace)
        variant_type_names = []
        if parent_type:
            variant_type_names.append(fmt_type_name(parent_type, namespace))

        def _is_struct_without_enumerated_subtypes(data_type):
            """
            :param data_type: any data type.
            :return: True if the given data type is a struct which has no enumerated subtypes.
            """
            return is_struct_type(data_type) and (
                not data_type.has_enumerated_subtypes())

        for variant in union_type.fields:
            if variant.doc:
                self._emit_tsdoc_header(variant.doc)
            variant_name = '%s%s' % (union_type_name, fmt_pascal(variant.name))
            variant_type_names.append(variant_name)

            is_struct_without_enumerated_subtypes = _is_struct_without_enumerated_subtypes(
                variant.data_type)

            if is_struct_without_enumerated_subtypes:
                self.emit(
                    'export interface %s extends %s {' %
                    (variant_name, fmt_type(variant.data_type, namespace)))
            else:
                self.emit('export interface %s {' % variant_name)

            with self.indent(dent=indent_spaces):
                # Since field contains non-alphanumeric character, we need to enclose
                # it in quotation marks.
                self.emit("'.tag': '%s';" % variant.name)
                if is_void_type(variant.data_type) is False and (
                        not is_struct_without_enumerated_subtypes):
                    self.emit(
                        "%s: %s;" %
                        (variant.name, fmt_type(variant.data_type, namespace)))
            self.emit('}')
            self.emit()

        if union_type.doc:
            self._emit_tsdoc_header(union_type.doc)
        self.emit('export type %s = %s;' %
                  (union_type_name, ' | '.join(variant_type_names)))
        self.emit()
Ejemplo n.º 21
0
    def _generate_union_class(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """Defines a Python class that represents a union in Stone."""
        self.emit(self._class_declaration_for_type(ns, data_type))
        with self.indent():
            self.emit('"""')
            if data_type.doc:
                self.emit_wrapped_text(
                    self.process_doc(data_type.doc, self._docf))
                self.emit()

            self.emit_wrapped_text(
                'This class acts as a tagged union. Only one of the ``is_*`` '
                'methods will return true. To get the associated value of a '
                'tag (if one exists), use the corresponding ``get_*`` method.')

            if data_type.has_documented_fields():
                self.emit()

            for field in data_type.fields:
                if not field.doc:
                    continue
                if is_void_type(field.data_type):
                    ivar_doc = ':ivar {}: {}'.format(
                        fmt_namespaced_var(ns.name, data_type.name,
                                           field.name),
                        self.process_doc(field.doc, self._docf))
                elif is_user_defined_type(field.data_type):
                    if data_type.namespace.name != ns.name:
                        formatted_var = fmt_namespaced_var(
                            ns.name, data_type.name, field.name)
                    else:
                        formatted_var = '{}.{}'.format(data_type.name,
                                                       fmt_var(field.name))
                    ivar_doc = ':ivar {} {}: {}'.format(
                        fmt_class(field.data_type.name), formatted_var,
                        self.process_doc(field.doc, self._docf))
                else:
                    ivar_doc = ':ivar {} {}: {}'.format(
                        self._python_type_mapping(ns, field.data_type),
                        fmt_namespaced_var(ns.name, data_type.name,
                                           field.name), field.doc)
                self.emit_wrapped_text(ivar_doc, subsequent_prefix='    ')
            self.emit('"""')
            self.emit()

            self._generate_union_class_vars(data_type)
            self._generate_union_class_variant_creators(ns, data_type)
            self._generate_union_class_is_set(data_type)
            self._generate_union_class_get_helpers(ns, data_type)
            self._generate_union_class_custom_annotations(ns, data_type)
        self.emit('{0}_validator = bv.Union({0})'.format(
            class_name_for_data_type(data_type)))
        self.emit()
Ejemplo n.º 22
0
 def _format_type_in_doc(self, namespace, data_type):
     """
     Returns a string that can be recognized by Sphinx as a type reference
     in a docstring.
     """
     if is_void_type(data_type):
         return 'None'
     elif is_user_defined_type(data_type):
         return ':class:`{}.{}.{}`'.format(
             self.args.types_package, namespace.name, fmt_type(data_type))
     else:
         return fmt_type(data_type)
Ejemplo n.º 23
0
 def _format_type_in_doc(self, namespace, data_type):
     """
     Returns a string that can be recognized by Sphinx as a type reference
     in a docstring.
     """
     if is_void_type(data_type):
         return 'None'
     elif is_user_defined_type(data_type):
         return ':class:`{}.{}.{}`'.format(
             self.args.types_package, namespace.name, fmt_type(data_type))
     else:
         return fmt_type(data_type)
Ejemplo n.º 24
0
    def _generate_route_m(self, route, namespace, route_args, extra_args,
                          task_type_name, func_suffix):
        """Generates route method implementation for the given route."""
        user_args = list(route_args)

        transport_args = [
            ('route', 'route'),
            ('arg', 'arg' if not is_void_type(route.arg_data_type) else 'nil'),
        ]

        for name, value, typ in extra_args:
            user_args.append((name, typ))
            transport_args.append((name, value))

        with self.block_func(
                func='{}{}'.format(fmt_route_func(route), func_suffix),
                args=fmt_func_args_declaration(user_args),
                return_type='{} *'.format(task_type_name)):
            self.emit('DBRoute *route = {}.{};'.format(
                fmt_route_obj_class(namespace.name),
                fmt_route_var(namespace.name, route)))
            if is_union_type(route.arg_data_type):
                self.emit('{} *arg = {};'.format(
                    fmt_class_prefix(route.arg_data_type),
                    fmt_var(route.arg_data_type.name)))
            elif not is_void_type(route.arg_data_type):
                init_call = fmt_func_call(
                    caller=fmt_alloc_call(
                        caller=fmt_class_prefix(route.arg_data_type)),
                    callee=self._cstor_name_from_fields_names(route_args),
                    args=fmt_func_args([(f[0], f[0]) for f in route_args]))
                self.emit('{} *arg = {};'.format(
                    fmt_class_prefix(route.arg_data_type), init_call))
            request_call = fmt_func_call(
                caller='self.client',
                callee='request{}'.format(
                    fmt_camel_upper(route.attrs.get('style'))),
                args=fmt_func_args(transport_args))
            self.emit('return {};'.format(request_call))
        self.emit()
Ejemplo n.º 25
0
    def _generate_route_m(self, route, namespace, route_args, extra_args,
                          task_type_name, func_suffix):
        """Generates route method implementation for the given route."""
        user_args = list(route_args)

        transport_args = [
            ('route', 'route'),
            ('arg', 'arg' if not is_void_type(route.arg_data_type) else 'nil'),
        ]

        for name, value, typ in extra_args:
            user_args.append((name, typ))
            transport_args.append((name, value))

        with self.block_func(
                func='{}{}'.format(fmt_var(route.name), func_suffix),
                args=fmt_func_args_declaration(user_args),
                return_type='{} *'.format(task_type_name)):
            self.emit('DBRoute *route = {}.{};'.format(
                fmt_route_obj_class(namespace.name),
                fmt_route_var(namespace.name, route.name)))
            if is_union_type(route.arg_data_type):
                self.emit('{} *arg = {};'.format(
                    fmt_class_prefix(route.arg_data_type),
                    fmt_var(route.arg_data_type.name)))
            elif not is_void_type(route.arg_data_type):
                init_call = fmt_func_call(
                    caller=fmt_alloc_call(
                        caller=fmt_class_prefix(route.arg_data_type)),
                    callee=self._cstor_name_from_fields_names(route_args),
                    args=fmt_func_args([(f[0], f[0]) for f in route_args]))
                self.emit('{} *arg = {};'.format(
                    fmt_class_prefix(route.arg_data_type), init_call))
            request_call = fmt_func_call(
                caller='self.client',
                callee='request{}'.format(
                    fmt_camel_upper(route.attrs.get('style'))),
                args=fmt_func_args(transport_args))
            self.emit('return {};'.format(request_call))
        self.emit()
Ejemplo n.º 26
0
 def _generate_union_class_symbol_creators(self, data_type):
     """
     Class attributes that represent a symbol are set after the union class
     definition.
     """
     class_name = fmt_class(data_type.name)
     lineno = self.lineno
     for field in data_type.fields:
         if is_void_type(field.data_type):
             field_name = fmt_func(field.name)
             self.emit("{0}.{1} = {0}('{1}')".format(class_name, field_name))
     if lineno != self.lineno:
         self.emit()
Ejemplo n.º 27
0
 def _generate_union_class_symbol_creators(self, data_type):
     """
     Class attributes that represent a symbol are set after the union class
     definition.
     """
     class_name = fmt_class(data_type.name)
     lineno = self.lineno
     for field in data_type.fields:
         if is_void_type(field.data_type):
             field_name = fmt_func(field.name)
             self.emit("{0}.{1} = {0}('{1}')".format(class_name, field_name))
     if lineno != self.lineno:
         self.emit()
Ejemplo n.º 28
0
    def _generate_union_type(self, union_type, indent_spaces):
        """
        Generates a TypeScript interface for a stone union.
        """
        # Emit an interface for each variant. TypeScript 2.0 supports these tagged unions.
        # https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#tagged-union-types
        parent_type = union_type.parent_type
        namespace = union_type.namespace
        union_type_name = fmt_type_name(union_type, namespace)
        variant_type_names = []
        if parent_type:
            variant_type_names.append(fmt_type_name(parent_type, namespace))

        def _is_struct_without_enumerated_subtypes(data_type):
            """
            :param data_type: any data type.
            :return: True if the given data type is a struct which has no enumerated subtypes.
            """
            return is_struct_type(data_type) and (
                not data_type.has_enumerated_subtypes())

        for variant in union_type.fields:
            if variant.doc:
                self._emit_tsdoc_header(variant.doc)
            variant_name = '%s%s' % (union_type_name, fmt_pascal(variant.name))
            variant_type_names.append(variant_name)

            is_struct_without_enumerated_subtypes = _is_struct_without_enumerated_subtypes(
                variant.data_type)

            if is_struct_without_enumerated_subtypes:
                self.emit('export interface %s extends %s {' % (
                    variant_name, fmt_type(variant.data_type, namespace)))
            else:
                self.emit('export interface %s {' % variant_name)

            with self.indent(dent=indent_spaces):
                # Since field contains non-alphanumeric character, we need to enclose
                # it in quotation marks.
                self.emit("'.tag': '%s';" % variant.name)
                if is_void_type(variant.data_type) is False and (
                    not is_struct_without_enumerated_subtypes
                ):
                    self.emit("%s: %s;" % (variant.name, fmt_type(variant.data_type, namespace)))
            self.emit('}')
            self.emit()

        if union_type.doc:
            self._emit_tsdoc_header(union_type.doc)
        self.emit('export type %s = %s;' % (union_type_name, ' | '.join(variant_type_names)))
        self.emit()
Ejemplo n.º 29
0
def make_test_field(field_name, stone_type, rust_generator, reference_impls):
    rust_name = rust_generator.field_name_raw(
        field_name) if field_name is not None else None
    typ, option = ir.unwrap_nullable(stone_type)

    inner = None
    value = None
    if ir.is_struct_type(typ):
        if typ.has_enumerated_subtypes():
            variant = typ.get_enumerated_subtypes()[0]
            inner = TestPolymorphicStruct(rust_generator, typ, reference_impls,
                                          variant)
        else:
            inner = TestStruct(rust_generator, typ, reference_impls)
        value = inner.value
    elif ir.is_union_type(typ):
        # Pick the first tag.
        # We could generate tests for them all, but it would lead to a huge explosion of tests, and
        # the types themselves are tested elsewhere.
        if len(typ.fields) == 0:
            # there must be a parent type; go for it
            variant = typ.all_fields[0]
        else:
            variant = typ.fields[0]
        inner = TestUnion(rust_generator, typ, reference_impls, variant)
        value = inner.value
    elif ir.is_list_type(typ):
        inner = TestList(rust_generator, typ.data_type, reference_impls)
        value = [inner.value]
    elif ir.is_map_type(typ):
        inner = TestMap(rust_generator, typ, reference_impls)
        value = inner.value
    elif ir.is_string_type(typ):
        if typ.pattern:
            value = Unregex(typ.pattern, typ.min_length).generate()
        elif typ.min_length:
            value = 'a' * typ.min_length
        else:
            value = 'something'
    elif ir.is_numeric_type(typ):
        value = typ.max_value or typ.maximum or 1e307
    elif ir.is_boolean_type(typ):
        value = True
    elif ir.is_timestamp_type(typ):
        value = datetime.datetime.utcfromtimestamp(2**33 - 1)
    elif ir.is_bytes_type(typ):
        value = bytes([0, 1, 2, 3, 4, 5])
    elif not ir.is_void_type(typ):
        raise RuntimeError(u'Error: unhandled field type of {}: {}'.format(
            field_name, typ))
    return TestField(rust_name, value, inner, typ, option)
Ejemplo n.º 30
0
    def _generate_union_class(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """Defines a Python class that represents a union in Stone."""
        self.emit(self._class_declaration_for_type(ns, data_type))
        with self.indent():
            self.emit('"""')
            if data_type.doc:
                self.emit_wrapped_text(
                    self.process_doc(data_type.doc, self._docf))
                self.emit()

            self.emit_wrapped_text(
                'This class acts as a tagged union. Only one of the ``is_*`` '
                'methods will return true. To get the associated value of a '
                'tag (if one exists), use the corresponding ``get_*`` method.')

            if data_type.has_documented_fields():
                self.emit()

            for field in data_type.fields:
                if not field.doc:
                    continue
                if is_void_type(field.data_type):
                    ivar_doc = ':ivar {}: {}'.format(
                        fmt_var(field.name),
                        self.process_doc(field.doc, self._docf))
                elif is_user_defined_type(field.data_type):
                    ivar_doc = ':ivar {} {}: {}'.format(
                        fmt_class(field.data_type.name),
                        fmt_var(field.name),
                        self.process_doc(field.doc, self._docf))
                else:
                    ivar_doc = ':ivar {} {}: {}'.format(
                        self._python_type_mapping(ns, field.data_type),
                        fmt_var(field.name), field.doc)
                self.emit_wrapped_text(ivar_doc, subsequent_prefix='    ')
            self.emit('"""')
            self.emit()

            self._generate_union_class_vars(data_type)
            self._generate_union_class_variant_creators(ns, data_type)
            self._generate_union_class_is_set(data_type)
            self._generate_union_class_get_helpers(ns, data_type)
            self._generate_union_class_repr(data_type)
        self.emit('{0}_validator = bv.Union({0})'.format(
            class_name_for_data_type(data_type)
        ))
        self.emit()
Ejemplo n.º 31
0
    def _generate_union_class_vars(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        lineno = self.lineno

        # Generate stubs for class variables so that IDEs like PyCharms have an
        # easier time detecting their existence.
        for field in data_type.fields:
            if is_void_type(field.data_type):
                field_name = fmt_var(field.name)
                field_type = class_name_for_data_type(data_type, ns)
                self.emit('{field_name} = ...  # type: {field_type}'.format(
                    field_name=field_name,
                    field_type=field_type,
                ))

        if lineno != self.lineno:
            self.emit()
Ejemplo n.º 32
0
    def _generate_union_class_vars(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        lineno = self.lineno

        # Generate stubs for class variables so that IDEs like PyCharms have an
        # easier time detecting their existence.
        for field in data_type.fields:
            if is_void_type(field.data_type):
                field_name = fmt_var(field.name)
                field_type = class_name_for_data_type(data_type, ns)
                self.emit('{field_name} = ...  # type: {field_type}'.format(
                    field_name=field_name,
                    field_type=field_type,
                ))

        if lineno != self.lineno:
            self.emit()
Ejemplo n.º 33
0
    def _generate_union_class_get_helpers(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """
        Generates the following section in the 'union Shape' example:
        def get_circle(self) -> float: ...
        """
        for field in data_type.fields:
            field_name = fmt_func(field.name)

            if not is_void_type(field.data_type):
                # generate getter for field
                val_type = self.map_stone_type_to_pep484_type(ns, field.data_type)

                self.emit('def get_{field_name}(self) -> {val_type}: ...'.format(
                    field_name=field_name,
                    val_type=val_type,
                ))
                self.emit()
Ejemplo n.º 34
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')
Ejemplo n.º 35
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
Ejemplo n.º 36
0
    def _generate_union_class_get_helpers(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """
        Generates the following section in the 'union Shape' example:
        def get_circle(self) -> float: ...
        """
        for field in data_type.fields:
            field_name = fmt_func(field.name)

            if not is_void_type(field.data_type):
                # generate getter for field
                val_type = self.map_stone_type_to_pep484_type(ns, field.data_type)

                self.emit('def get_{field_name}(self) -> {val_type}: ...'.format(
                    field_name=field_name,
                    val_type=val_type,
                ))
                self.emit()
Ejemplo n.º 37
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
Ejemplo n.º 38
0
    def emit_asserts(self, codegen, expression_path):
        if expression_path[0] == '(' and expression_path[-1] == ')':
                expression_path = expression_path[1:-1]  # strip off superfluous parens

        with codegen.block(u'match {}'.format(expression_path)):
            if ir.is_void_type(self._variant_type):
                codegen.emit(u'::dropbox_sdk::{}::{}::{} => (),'.format(
                    self._rust_namespace_name,
                    self._rust_name,
                    self._rust_variant_name))
            else:
                with codegen.block(u'::dropbox_sdk::{}::{}::{}(ref v) =>'.format(
                        self._rust_namespace_name,
                        self._rust_name,
                        self._rust_variant_name)):
                    self._inner_value.emit_assert(codegen, '(*v)')

            if self.is_open():
                codegen.emit(u'_ => panic!("wrong variant")')
Ejemplo n.º 39
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), ':')
Ejemplo n.º 40
0
    def _generate_union_class_vars(self, data_type):
        """
        Adds a _catch_all_ attribute to each class. Also, adds a placeholder
        attribute for the construction of union members of void type.
        """
        lineno = self.lineno
        if data_type.catch_all_field:
            self.emit("_catch_all = '%s'" % data_type.catch_all_field.name)
        elif not data_type.parent_type:
            self.emit('_catch_all = None')

        # Generate stubs for class variables so that IDEs like PyCharms have an
        # easier time detecting their existence.
        for field in data_type.fields:
            if is_void_type(field.data_type):
                field_name = fmt_var(field.name)
                self.emit('# Attribute is overwritten below the class definition')
                self.emit('{} = None'.format(field_name))

        if lineno != self.lineno:
            self.emit()
Ejemplo n.º 41
0
    def _generate_union_class_vars(self, data_type):
        """
        Adds a _catch_all_ attribute to each class. Also, adds a placeholder
        attribute for the construction of union members of void type.
        """
        lineno = self.lineno
        if data_type.catch_all_field:
            self.emit("_catch_all = '%s'" % data_type.catch_all_field.name)
        elif not data_type.parent_type:
            self.emit('_catch_all = None')

        # Generate stubs for class variables so that IDEs like PyCharms have an
        # easier time detecting their existence.
        for field in data_type.fields:
            if is_void_type(field.data_type):
                field_name = fmt_var(field.name)
                self.emit('# Attribute is overwritten below the class definition')
                self.emit('{} = None'.format(field_name))

        if lineno != self.lineno:
            self.emit()
Ejemplo n.º 42
0
    def _generate_union_class_variant_creators(self, ns, data_type):
        # type: (ApiNamespace, Union) -> None
        """
        Generate the following section in the 'union Shape' example:
        @classmethod
        def circle(cls, val: float) -> Shape: ...
        """
        union_type = class_name_for_data_type(data_type)

        for field in data_type.fields:
            if not is_void_type(field.data_type):
                field_name_reserved_check = fmt_func(field.name, check_reserved=True)
                val_type = self.map_stone_type_to_pep484_type(ns, field.data_type)

                self.emit('@classmethod')
                self.emit('def {field_name}(cls, val: {val_type}) -> {union_type}: ...'.format(
                    field_name=field_name_reserved_check,
                    val_type=val_type,
                    union_type=union_type,
                ))
                self.emit()
Ejemplo n.º 43
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), ':')
Ejemplo n.º 44
0
    def _generate_struct_class_init(self, ns, struct):
        # type: (ApiNamespace, Struct) -> None
        args = []
        for field in struct.all_fields:
            field_name_reserved_check = fmt_var(field.name, True)
            field_type = self.map_stone_type_to_pep484_type(
                ns, field.data_type)

            # The constructor takes all possible fields as optional arguments
            if not is_nullable_type(field.data_type) and not is_void_type(
                    field.data_type):
                self.import_tracker._register_typing_import('Optional')
                field_type = 'Optional[{}]'.format(field_type)

            args.append("{field_name}: {field_type} = ...".format(
                field_name=field_name_reserved_check,
                field_type=field_type,
            ))

        self.generate_multiline_list(before='def __init__',
                                     items=["self"] + args,
                                     after=' -> None: ...')
Ejemplo n.º 45
0
    def _generate_struct_class_properties(self, ns, struct):
        # type: (ApiNamespace, Struct) -> None
        to_emit = []  # type: typing.List[typing.Text]
        for field in struct.all_fields:
            field_name_reserved_check = fmt_func(field.name, check_reserved=True)
            setter_field_type = self.map_stone_type_to_pep484_type(ns, field.data_type)

            # The field might not have been set since it is optional in the constructor.
            getter_field_type = setter_field_type
            if not is_nullable_type(field.data_type) and not is_void_type(field.data_type):
                self.import_tracker._register_typing_import('Optional')
                getter_field_type = 'Optional[{}]'.format(setter_field_type)

            to_emit.extend(
                self.property_template.format(
                    field_name=field_name_reserved_check,
                    getter_field_type=getter_field_type,
                    setter_field_type=setter_field_type
                ).split("\n")
            )

        for s in to_emit:
            self.emit(s)
Ejemplo n.º 46
0
def fmt_route_type(data_type, tag=False, has_default=False):
    data_type, nullable = unwrap_nullable(data_type)

    if is_user_defined_type(data_type):
        result = '{} *'.format(fmt_class_prefix(data_type))
    else:
        result = _primitive_table_user_interface.get(data_type.__class__,
                                                     fmt_class(data_type.name))

        if is_list_type(data_type):
            data_type, _ = unwrap_nullable(data_type.data_type)
            result = result + '<{}> *'.format(fmt_type(data_type))
        elif is_map_type(data_type):
            data_type, _ = unwrap_nullable(data_type.value_data_type)
            result = result + '<NSString *, {}>'.format(fmt_type(data_type))

    if is_user_defined_type(data_type) and tag:
        if nullable or has_default:
            result = 'nullable ' + result
        elif not is_void_type(data_type):
            result += ''

    return result
Ejemplo n.º 47
0
def fmt_route_type(data_type, tag=False, has_default=False):
    data_type, nullable = unwrap_nullable(data_type)

    if is_user_defined_type(data_type):
        result = '{} *'.format(fmt_class_prefix(data_type))
    else:
        result = _primitive_table_user_interface.get(data_type.__class__,
                                                     fmt_class(data_type.name))

        if is_list_type(data_type):
            data_type, _ = unwrap_nullable(data_type.data_type)
            result = result + '<{}> *'.format(fmt_type(data_type))
        elif is_map_type(data_type):
            data_type, _ = unwrap_nullable(data_type.value_data_type)
            result = result + '<NSString *, {}>'.format(fmt_type(data_type))

    if is_user_defined_type(data_type) and tag:
        if nullable or has_default:
            result = 'nullable ' + result
        elif not is_void_type(data_type):
            result += ''

    return result
Ejemplo n.º 48
0
    def _generate_union_class_get_helpers(self, ns, data_type):
        """
        These are the getters used to access the value of a variant, once
        the tag has been switched on.
        """
        for field in data_type.fields:
            field_name = fmt_func(field.name)

            if not is_void_type(field.data_type):
                # generate getter for field
                self.emit('def get_{}(self):'.format(field_name))
                with self.indent():
                    if is_nullable_type(field.data_type):
                        field_dt = field.data_type.data_type
                    else:
                        field_dt = field.data_type
                    self.emit('"""')
                    if field.doc:
                        self.emit_wrapped_text(
                            self.process_doc(field.doc, self._docf))
                        self.emit()
                    self.emit("Only call this if :meth:`is_%s` is true." %
                              field_name)
                    # Sphinx wants an extra line between the text and the
                    # rtype declaration.
                    self.emit()
                    self.emit(':rtype: {}'.format(
                        self._python_type_mapping(ns, field_dt)))
                    self.emit('"""')

                    self.emit('if not self.is_{}():'.format(field_name))
                    with self.indent():
                        self.emit(
                            'raise AttributeError("tag \'{}\' not set")'.format(
                                field_name))
                    self.emit('return self._value')
                self.emit()
Ejemplo n.º 49
0
    def _generate_union_class_get_helpers(self, ns, data_type):
        """
        These are the getters used to access the value of a variant, once
        the tag has been switched on.
        """
        for field in data_type.fields:
            field_name = fmt_func(field.name)

            if not is_void_type(field.data_type):
                # generate getter for field
                self.emit('def get_{}(self):'.format(field_name))
                with self.indent():
                    if is_nullable_type(field.data_type):
                        field_dt = field.data_type.data_type
                    else:
                        field_dt = field.data_type
                    self.emit('"""')
                    if field.doc:
                        self.emit_wrapped_text(
                            self.process_doc(field.doc, self._docf))
                        self.emit()
                    self.emit("Only call this if :meth:`is_%s` is true." %
                              field_name)
                    # Sphinx wants an extra line between the text and the
                    # rtype declaration.
                    self.emit()
                    self.emit(':rtype: {}'.format(
                        self._python_type_mapping(ns, field_dt)))
                    self.emit('"""')

                    self.emit('if not self.is_{}():'.format(field_name))
                    with self.indent():
                        self.emit(
                            'raise AttributeError("tag \'{}\' not set")'.format(
                                field_name))
                    self.emit('return self._value')
                self.emit()
Ejemplo n.º 50
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

            txt = 'return client.request({})'.format(
                self._func_args(return_args, not_init=True)
            )

            self.emit(txt)
        self.emit()
Ejemplo n.º 51
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:`{}`'.format(self.args.error_class_path))
            self.emit()
            # To provide more clarity to a dev who reads the docstring, suggest
            # the route's error class. This is confusing, however, because we
            # don't know where the error object that's raised will store
            # the more detailed route error defined in stone.
            error_class_name = self.args.error_class_path.rsplit('.', 1)[-1]
            self.emit('If this raises, {} will contain:'.format(error_class_name))
            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('"""')
Ejemplo n.º 52
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 stone.ir.ApiRoute route: IR node for the route.
        :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 and not download_to_file:
                extra_return_arg = ':class:`requests.models.Response`'
                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(
                        fmt_namespace(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(fmt_namespace(namespace.name),
                               fmt_func(route.name, version=route.version)),
                "'{}'".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()
Ejemplo n.º 53
0
 def _format_tag_type(self, namespace, data_type):  # pylint: disable=unused-argument
     if is_void_type(data_type):
         return ''
     else:
         return '({})'.format(fmt_type(data_type))
Ejemplo n.º 54
0
def map_stone_type_to_python_type(ns, data_type, override_dict=None):
    # type: (ApiNamespace, DataType, typing.Optional[OverrideDefaultTypesDict]) -> str
    """
    Args:
        override_dict: lets you override the default behavior for a given type by hooking into
            a callback. (Currently only hooked up for stone's List and Nullable)
    """
    override_dict = override_dict or {}

    if is_string_type(data_type):
        return 'str'
    elif is_bytes_type(data_type):
        return 'bytes'
    elif is_boolean_type(data_type):
        return 'bool'
    elif is_float_type(data_type):
        return 'float'
    elif is_integer_type(data_type):
        return 'long'
    elif is_void_type(data_type):
        return 'None'
    elif is_timestamp_type(data_type):
        timestamp_override = override_dict.get(Timestamp, None)
        if timestamp_override:
            return timestamp_override(ns, data_type, override_dict)
        return 'datetime.datetime'
    elif is_alias(data_type):
        alias_type = cast(Alias, data_type)
        return map_stone_type_to_python_type(ns, alias_type.data_type, override_dict)
    elif is_user_defined_type(data_type):
        user_defined_type = cast(UserDefined, data_type)
        class_name = class_name_for_data_type(user_defined_type)
        if user_defined_type.namespace.name != ns.name:
            return '%s.%s_validator' % (
                user_defined_type.namespace.name, class_name)
        else:
            return class_name
    elif is_list_type(data_type):
        list_type = cast(List, data_type)
        if List in override_dict:
            return override_dict[List](ns, list_type.data_type, override_dict)

        # PyCharm understands this description format for a list
        return 'list of [{}]'.format(
            map_stone_type_to_python_type(ns, list_type.data_type, override_dict)
        )
    elif is_map_type(data_type):
        map_type = cast(Map, data_type)
        if Map in override_dict:
            return override_dict[Map](
                ns,
                data_type,
                override_dict
            )

        return 'dict of [{}:{}]'.format(
            map_stone_type_to_python_type(ns, map_type.key_data_type, override_dict),
            map_stone_type_to_python_type(ns, map_type.value_data_type, override_dict)
        )

    elif is_nullable_type(data_type):
        nullable_type = cast(Nullable, data_type)
        if Nullable in override_dict:
            return override_dict[Nullable](ns, nullable_type.data_type, override_dict)

        return 'Optional[{}]'.format(
            map_stone_type_to_python_type(ns, nullable_type.data_type, override_dict)
        )
    else:
        raise TypeError('Unknown data type %r' % data_type)
Ejemplo n.º 55
0
def generate_validator_constructor(ns, data_type):
    """
    Given a Stone data type, returns a string that can be used to construct
    the appropriate validation object in Python.
    """
    dt, nullable_dt = unwrap_nullable(data_type)
    if is_list_type(dt):
        v = generate_func_call(
            'bv.List',
            args=[
                generate_validator_constructor(ns, dt.data_type)],
            kwargs=[
                ('min_items', dt.min_items),
                ('max_items', dt.max_items)],
        )
    elif is_map_type(dt):
        v = generate_func_call(
            'bv.Map',
            args=[
                generate_validator_constructor(ns, dt.key_data_type),
                generate_validator_constructor(ns, dt.value_data_type),
            ]
        )
    elif is_numeric_type(dt):
        v = generate_func_call(
            'bv.{}'.format(dt.name),
            kwargs=[
                ('min_value', dt.min_value),
                ('max_value', dt.max_value)],
        )
    elif is_string_type(dt):
        pattern = None
        if dt.pattern is not None:
            pattern = repr(dt.pattern)
        v = generate_func_call(
            'bv.String',
            kwargs=[
                ('min_length', dt.min_length),
                ('max_length', dt.max_length),
                ('pattern', pattern)],
        )
    elif is_timestamp_type(dt):
        v = generate_func_call(
            'bv.Timestamp',
            args=[repr(dt.format)],
        )
    elif is_user_defined_type(dt):
        v = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            v = '{}.{}'.format(dt.namespace.name, v)
    elif is_alias(dt):
        # Assume that the alias has already been declared elsewhere.
        name = fmt_class(dt.name) + '_validator'
        if ns.name != dt.namespace.name:
            name = '{}.{}'.format(dt.namespace.name, name)
        v = name
    elif is_boolean_type(dt) or is_bytes_type(dt) or is_void_type(dt):
        v = generate_func_call('bv.{}'.format(dt.name))
    else:
        raise AssertionError('Unsupported data type: %r' % dt)

    if nullable_dt:
        return generate_func_call('bv.Nullable', args=[v])
    else:
        return v