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, '')
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")')
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")')
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)
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
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 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
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
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
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
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)))
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
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
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
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()
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()
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)
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
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_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)
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)
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)
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)
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)
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)
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')
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)
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), ':')
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), ':')
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))
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_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()
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()
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
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()
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()
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 _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)
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()
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()
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('"""')