def _get_imports_h(self, data_types): """Emits all necessary header file imports for the given Stone data type.""" if not isinstance(data_types, list): data_types = [data_types] import_classes = [] for data_type in data_types: if is_user_defined_type(data_type): import_classes.append(fmt_class_prefix(data_type)) 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)) import_classes = list(set(import_classes)) import_classes.sort() return import_classes
def _get_imports_h(self, data_types): """Emits all necessary header file imports for the given Stone data type.""" if not isinstance(data_types, list): data_types = [data_types] import_classes = [] for data_type in data_types: if is_user_defined_type(data_type): import_classes.append(fmt_class_prefix(data_type)) 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)) import_classes = list(set(import_classes)) import_classes.sort() return import_classes
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 _python_type_mapping(self, ns, data_type): """Map Stone data types to their most natural equivalent in Python for documentation purposes.""" 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): return 'datetime.datetime' elif is_alias(data_type): return self._python_type_mapping(ns, data_type.data_type) elif is_user_defined_type(data_type): class_name = class_name_for_data_type(data_type) if data_type.namespace.name != ns.name: return '%s.%s_validator' % ( data_type.namespace.name, class_name) else: return class_name elif is_list_type(data_type): # PyCharm understands this description format for a list return 'list of [{}]'.format(self._python_type_mapping( ns, data_type.data_type)) elif is_nullable_type(data_type): return 'Optional[{}]'.format( self._python_type_mapping(ns, data_type.data_type)) else: raise TypeError('Unknown data type %r' % data_type)
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 _fmt_default(self, data_type): data_type, nullable = unwrap_nullable(data_type) result = 'DEFAULT' if nullable: return 'nil' if is_user_defined_type(data_type): result = fmt_func_call(fmt_alloc_call(fmt_class_prefix(data_type)), 'init', []) elif is_list_type(data_type): result = fmt_func_call(fmt_alloc_call('NSArray'), 'init', []) elif is_numeric_type(data_type): if is_float_type(data_type): result = '[NSNumber numberWithDouble:5]' else: result = '[NSNumber numberWithInt:5]' elif is_timestamp_type(data_type): result = '[[NSDateFormatter new] setDateFormat:[self convertFormat:@"test"]]' elif is_string_type(data_type): result = '@"teststring"' elif is_boolean_type(data_type): result = '@YES' return result
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 _python_type_mapping(self, ns, data_type): """Map Stone data types to their most natural equivalent in Python for documentation purposes.""" 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): return 'datetime.datetime' elif is_alias(data_type): return self._python_type_mapping(ns, data_type.data_type) elif is_user_defined_type(data_type): class_name = class_name_for_data_type(data_type) if data_type.namespace.name != ns.name: return '%s.%s_validator' % ( data_type.namespace.name, class_name) else: return class_name elif is_list_type(data_type): # PyCharm understands this description format for a list return 'list of [{}]'.format(self._python_type_mapping( ns, data_type.data_type)) elif is_nullable_type(data_type): return 'Optional[{}]'.format( self._python_type_mapping(ns, data_type.data_type)) else: raise TypeError('Unknown data type %r' % data_type)
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 _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)
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)
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_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)
def _generate_alias_definition(self, namespace, alias): # type: (ApiNamespace, Alias) -> None unwrapped_dt, _ = unwrap_aliases(alias) if is_user_defined_type(unwrapped_dt): # If the alias is to a composite type, we want to alias the # generated class as well. self.emit('{} = {}'.format( alias.name, class_name_for_data_type(alias.data_type, namespace)))
def _generate_alias_definition(self, namespace, alias): # type: (ApiNamespace, Alias) -> None unwrapped_dt, _ = unwrap_aliases(alias) if is_user_defined_type(unwrapped_dt): # If the alias is to a composite type, we want to alias the # generated class as well. self.emit('{} = {}'.format( alias.name, class_name_for_data_type(alias.data_type, namespace)))
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_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
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_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
def fmt_serial_obj(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = fmt_serial_class(fmt_class_prefix(data_type)) else: result = _serial_table.get(data_type.__class__, fmt_class(data_type.name)) return result
def fmt_serial_obj(data_type): data_type, _ = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = fmt_serial_class(fmt_class_prefix(data_type)) else: result = _serial_table.get( data_type.__class__, fmt_class(data_type.name)) return result
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)) if is_user_defined_type(data_type) and tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
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)) if is_user_defined_type(data_type) and tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
def fmt_type_name(data_type): """ Returns the JSDoc name for the given data type. (Does not attempt to enumerate subtypes.) """ if is_user_defined_type(data_type): return fmt_pascal('%s%s' % (data_type.namespace.name, data_type.name)) else: fmted_type = _base_type_table.get(data_type.__class__, 'Object') if is_list_type(data_type): fmted_type += '.<' + fmt_type(data_type.data_type) + '>' return fmted_type
def _generate_route(self, route_schema, namespace, route, extra_args): function_name = fmt_func(namespace.name + '_' + route.name) self.emit() self.emit('/**') if route.doc: self.emit_wrapped_text(self.process_doc(route.doc, self._docf), prefix=' * ') if self.args.class_name: self.emit(' * @function {}#{}'.format(self.args.class_name, function_name)) if route.deprecated: self.emit(' * @deprecated') self.emit(' * @arg {%s} arg - The request parameters.' % fmt_type(route.arg_data_type)) if is_user_defined_type(route.arg_data_type): for attr_key in route.attrs: if attr_key not in extra_args: continue attr_val = route.attrs[attr_key] if attr_val in extra_args[attr_key]: arg_name, arg_type, arg_docstring = extra_args[attr_key][attr_val] field_docstring = '@arg {%s} arg.%s' % (arg_type, arg_name) if arg_docstring: field_docstring += ' - %s' % arg_docstring self.emit_wrapped_text(field_docstring, prefix=' * ') for field in route.arg_data_type.all_fields: field_doc = ' - ' + field.doc if field.doc else '' field_type, nullable, _ = unwrap(field.data_type) field_js_type = fmt_type(field_type) if nullable: field_js_type += '|null' self.emit_wrapped_text( '@arg {%s} arg.%s%s' % (field_js_type, field.name, self.process_doc(field_doc, self._docf)), prefix=' * ') self.emit(' * @returns {%s}' % fmt_type(route.result_data_type)) self.emit(' */') self.emit('routes.%s = function (arg) {' % function_name) with self.indent(dent=2): url = '{}/{}'.format(namespace.name, route.name) if route_schema.fields: additional_args = [] for field in route_schema.fields: additional_args.append(fmt_obj(route.attrs[field.name])) self.emit( "return this.request('{}', arg, {});".format( url, ', '.join(additional_args))) else: self.emit( 'return this.request("%s", arg);' % url) self.emit('};')
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)
def fmt_type_name(data_type): """ Returns the JSDoc name for the given data type. (Does not attempt to enumerate subtypes.) """ if is_user_defined_type(data_type): return fmt_pascal('%s%s' % (data_type.namespace.name, data_type.name)) else: fmted_type = _base_type_table.get(data_type.__class__, 'Object') if is_list_type(data_type): fmted_type += '.<' + fmt_type(data_type.data_type) + '>' return fmted_type
def fmt_type(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}'.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '<{}>'.format(fmt_type(data_type.data_type)) return result if not nullable else result + '?'
def _generate_alias_definition(self, namespace, alias): v = generate_validator_constructor(namespace, alias.data_type) if alias.doc: self.emit_wrapped_text( self.process_doc(alias.doc, self._docf), prefix='# ') self.emit('{}_validator = {}'.format(alias.name, v)) unwrapped_dt, _ = unwrap_aliases(alias) if is_user_defined_type(unwrapped_dt): # If the alias is to a composite type, we want to alias the # generated class as well. self.emit('{} = {}'.format( alias.name, class_name_for_data_type(alias.data_type, namespace)))
def _class_declaration_for_type(self, ns, data_type): assert is_user_defined_type(data_type), \ 'Expected struct, got %r' % type(data_type) if data_type.parent_type: extends = class_name_for_data_type(data_type.parent_type, ns) else: if is_union_type(data_type): # Use a handwritten base class extends = 'bb.Union' else: extends = 'object' return 'class {}({}):'.format( class_name_for_data_type(data_type), extends)
def fmt_type(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}'.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '<{}>'.format(fmt_type(data_type.data_type)) return result if not nullable else result + '?'
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)
def _generate_alias_definition(self, namespace, alias): v = generate_validator_constructor(namespace, alias.data_type) if alias.doc: self.emit_wrapped_text( self.process_doc(alias.doc, self._docf), prefix='# ') self.emit('{}_validator = {}'.format(alias.name, v)) unwrapped_dt, _ = unwrap_aliases(alias) if is_user_defined_type(unwrapped_dt): # If the alias is to a composite type, we want to alias the # generated class as well. self.emit('{} = {}'.format( alias.name, class_name_for_data_type(alias.data_type, namespace)))
def _class_declaration_for_type(self, ns, data_type): assert is_user_defined_type(data_type), \ 'Expected struct, got %r' % type(data_type) if data_type.parent_type: extends = class_name_for_data_type(data_type.parent_type, ns) else: if is_union_type(data_type): # Use a handwritten base class extends = 'bb.Union' else: extends = 'object' return 'class {}({}):'.format( class_name_for_data_type(data_type), extends)
def fmt_class_type(data_type): 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.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)) return result
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()
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()
def fmt_type_name(data_type, inside_namespace=None): """ Produces a TypeScript type name for the given data type. inside_namespace should be set to the namespace that the reference occurs in, or None if this parameter is not relevant. """ if is_user_defined_type(data_type) or is_alias(data_type): if data_type.namespace == inside_namespace: return data_type.name else: return '%s.%s' % (data_type.namespace.name, data_type.name) else: fmted_type = _base_type_table.get(data_type.__class__, 'Object') if is_list_type(data_type): fmted_type += '<' + fmt_type(data_type.data_type, inside_namespace) + '>' return fmted_type
def class_name_for_data_type(data_type, ns=None): """ Returns the name of the Python class that maps to a user-defined type. The name is identical to the name in the spec. If ``ns`` is set to a Namespace and the namespace of `data_type` does not match, then a namespace prefix is added to the returned name. For example, ``foreign_ns.TypeName``. """ assert is_user_defined_type(data_type) or is_alias(data_type), \ 'Expected composite type, got %r' % type(data_type) name = fmt_class(data_type.name) if ns and data_type.namespace != ns: # If from an imported namespace, add a namespace prefix. name = '{}.{}'.format(data_type.namespace.name, name) return name
def class_name_for_data_type(data_type, ns=None): """ Returns the name of the Python class that maps to a user-defined type. The name is identical to the name in the spec. If ``ns`` is set to a Namespace and the namespace of `data_type` does not match, then a namespace prefix is added to the returned name. For example, ``foreign_ns.TypeName``. """ assert is_user_defined_type(data_type) or is_alias(data_type), \ 'Expected composite type, got %r' % type(data_type) name = fmt_class(data_type.name) if ns and data_type.namespace != ns: # If from an imported namespace, add a namespace prefix. name = '{}.{}'.format(data_type.namespace.name, name) return name
def fmt_type_name(data_type, inside_namespace=None): """ Produces a TypeScript type name for the given data type. inside_namespace should be set to the namespace that the reference occurs in, or None if this parameter is not relevant. """ if is_user_defined_type(data_type) or is_alias(data_type): if data_type.namespace == inside_namespace: return data_type.name else: return '%s.%s' % (data_type.namespace.name, data_type.name) else: fmted_type = _base_type_table.get(data_type.__class__, 'Object') if is_list_type(data_type): fmted_type += '<' + fmt_type(data_type.data_type, inside_namespace) + '>' return fmted_type
def _swift_type_mapping(self, data_type, serializer=False): suffix = 'Serializer' if serializer else '' if is_nullable_type(data_type): data_type = data_type.data_type nullable = True else: nullable = False if is_list_type(data_type): ret = 'Array{}<{}>'.format( suffix, self._swift_type_mapping(data_type.data_type, serializer) ) suffix = '' elif is_string_type(data_type): ret = 'String' elif is_timestamp_type(data_type): ret = 'NSDate' elif is_boolean_type(data_type): ret = 'Bool' elif is_bytes_type(data_type): ret = 'NSData' elif is_void_type(data_type): ret = 'Void' elif isinstance(data_type, Int32): ret = 'Int32' elif isinstance(data_type, Int64): ret = 'Int64' elif isinstance(data_type, UInt32): ret = 'UInt32' elif isinstance(data_type, UInt64): ret = 'UInt64' elif isinstance(data_type, Float32): ret = 'Float' elif isinstance(data_type, Float64): ret = 'Double' elif is_user_defined_type(data_type): ret = '{}.{}'.format(fmt_class(data_type.namespace.name), self.class_data_type(data_type)) ret += suffix if nullable: if serializer: ret = 'NullableSerializer<{}>'.format(ret) else: ret += '?' return ret
def fmt_class_type(data_type, suppress_ptr=False): data_type, _ = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}'.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get( data_type.__class__, fmt_class(data_type.name)) if suppress_ptr: result = result.replace(' *', '') result = result.replace('*', '') if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) result = result + '<{}>'.format(fmt_type(data_type)) return result
def fmt_serial_obj(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}Serializer()' result = result.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _serial_type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '({})'.format(fmt_serial_obj(data_type.data_type)) elif is_timestamp_type(data_type): result = result + '("{}")'.format(data_type.format) else: result = 'Serialization._{}'.format(result) return result if not nullable else 'NullableSerializer({})'.format(result)
def fmt_serial_obj(data_type): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): result = '{}.{}Serializer()' result = result.format(fmt_class(data_type.namespace.name), fmt_class(data_type.name)) else: result = _serial_type_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): result = result + '({})'.format(fmt_serial_obj(data_type.data_type)) elif is_timestamp_type(data_type): result = result + '("{}")'.format(data_type.format) else: result = 'Serialization._{}'.format(result) return result if not nullable else 'NullableSerializer({})'.format(result)
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 fmt_type(data_type, tag=False, has_default=False, no_ptr=False, is_prop=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): base = '{}' if no_ptr else '{} *' result = base.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) base = '<{}>' if no_ptr else '<{}> *' result = result + base.format(fmt_type(data_type)) if tag: if (nullable or has_default) and not is_prop: result = 'nullable ' + result return result
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 _serializer_obj(self, data_type): if is_nullable_type(data_type): data_type = data_type.data_type nullable = True else: nullable = False if is_list_type(data_type): ret = 'ArraySerializer({})'.format( self._serializer_obj(data_type.data_type)) elif is_string_type(data_type): ret = 'Serialization._StringSerializer' elif is_timestamp_type(data_type): ret = 'NSDateSerializer("{}")'.format(data_type.format) elif is_boolean_type(data_type): ret = 'Serialization._BoolSerializer' elif is_bytes_type(data_type): ret = 'Serialization._NSDataSerializer' elif is_void_type(data_type): ret = 'Serialization._VoidSerializer' elif isinstance(data_type, Int32): ret = 'Serialization._Int32Serializer' elif isinstance(data_type, Int64): ret = 'Serialization._Int64Serializer' elif isinstance(data_type, UInt32): ret = 'Serialization._UInt32Serializer' elif isinstance(data_type, UInt64): ret = 'Serialization._UInt64Serializer' elif isinstance(data_type, Float32): ret = 'Serialization._FloatSerializer' elif isinstance(data_type, Float64): ret = 'Serialization._DoubleSerializer' elif is_user_defined_type(data_type): ret = "{}.{}Serializer()".format(fmt_class(data_type.namespace.name), self.class_data_type(data_type)) if nullable: ret = 'NullableSerializer({})'.format(ret) return ret
def fmt_type(data_type, tag=False, has_default=False, no_ptr=False): data_type, nullable = unwrap_nullable(data_type) if is_user_defined_type(data_type): base = '{}' if no_ptr else '{} *' result = base.format(fmt_class_prefix(data_type)) else: result = _primitive_table.get(data_type.__class__, fmt_class(data_type.name)) if is_list_type(data_type): data_type, _ = unwrap_nullable(data_type.data_type) base = '<{}>' if no_ptr else '<{}> *' result = result + base.format(fmt_type(data_type)) if tag: if nullable or has_default: result += ' _Nullable' elif not is_void_type(data_type): result += ' _Nonnull' return result
def _parse_extra_args(self, api, extra_args_raw): """ Parses extra arguments into a map keyed on particular data types. """ extra_args = {} def invalid(msg, extra_arg_raw): print('Invalid --extra-arg:%s: %s' % (msg, extra_arg_raw), file=sys.stderr) sys.exit(1) for extra_arg_raw in extra_args_raw: try: extra_arg = json.loads(extra_arg_raw) except ValueError as e: invalid(str(e), extra_arg_raw) # Validate extra_arg JSON blob if 'match' not in extra_arg: invalid('No match key', extra_arg_raw) elif (not isinstance(extra_arg['match'], list) or len(extra_arg['match']) != 2): invalid('match key is not a list of two strings', extra_arg_raw) elif (not isinstance(extra_arg['match'][0], six.text_type) or not isinstance(extra_arg['match'][1], six.text_type)): print(type(extra_arg['match'][0])) invalid('match values are not strings', extra_arg_raw) elif 'arg_name' not in extra_arg: invalid('No arg_name key', extra_arg_raw) elif not isinstance(extra_arg['arg_name'], six.text_type): invalid('arg_name is not a string', extra_arg_raw) elif 'arg_type' not in extra_arg: invalid('No arg_type key', extra_arg_raw) elif not isinstance(extra_arg['arg_type'], six.text_type): invalid('arg_type is not a string', extra_arg_raw) elif ('arg_docstring' in extra_arg and not isinstance(extra_arg['arg_docstring'], six.text_type)): invalid('arg_docstring is not a string', extra_arg_raw) attr_key, attr_val = extra_arg['match'][0], extra_arg['match'][1] extra_args.setdefault(attr_key, {})[attr_val] = \ (extra_arg['arg_name'], extra_arg['arg_type'], extra_arg.get('arg_docstring')) # Extra arguments, keyed on data type objects. extra_args_for_types = {} # Locate data types that contain extra arguments for namespace in api.namespaces.values(): for route in namespace.routes: extra_parameters = [] if is_user_defined_type(route.arg_data_type): for attr_key in route.attrs: if attr_key not in extra_args: continue attr_val = route.attrs[attr_key] if attr_val in extra_args[attr_key]: extra_parameters.append(extra_args[attr_key][attr_val]) if len(extra_parameters) > 0: extra_args_for_types[route.arg_data_type] = extra_parameters return extra_args_for_types
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 _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 _generate_struct_class_properties(self, ns, data_type): """ Each field of the struct has a corresponding setter and getter. The setter validates the value being set. """ for field in data_type.fields: 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 dt_nullable = True else: field_dt = field.data_type dt_nullable = False # generate getter for field self.emit('@property') self.emit('def {}(self):'.format(field_name_reserved_check)) with self.indent(): self.emit('"""') if field.doc: self.emit_wrapped_text( self.process_doc(field.doc, self._docf)) # 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 self._{}_present:'.format(field_name)) with self.indent(): self.emit('return self._{}_value'.format(field_name)) self.emit('else:') with self.indent(): if dt_nullable: self.emit('return None') elif field.has_default: self.emit('return {}'.format( self._generate_python_value(ns, field.default))) else: self.emit( "raise AttributeError(\"missing required field '%s'\")" % field_name ) self.emit() # generate setter for field self.emit('@{}.setter'.format(field_name_reserved_check)) self.emit('def {}(self, val):'.format(field_name_reserved_check)) with self.indent(): if dt_nullable: self.emit('if val is None:') with self.indent(): self.emit('del self.{}'.format(field_name_reserved_check)) self.emit('return') if is_user_defined_type(field_dt): self.emit('self._%s_validator.validate_type_only(val)' % field_name) else: self.emit('val = self._{}_validator.validate(val)'.format(field_name)) self.emit('self._{}_value = val'.format(field_name)) self.emit('self._{}_present = True'.format(field_name)) self.emit() # generate deleter for field self.emit('@{}.deleter'.format(field_name_reserved_check)) self.emit('def {}(self):'.format(field_name_reserved_check)) with self.indent(): self.emit('self._{}_value = None'.format(field_name)) self.emit('self._{}_present = False'.format(field_name)) self.emit()
def _generate_struct_class_properties(self, ns, data_type): """ Each field of the struct has a corresponding setter and getter. The setter validates the value being set. """ for field in data_type.fields: 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 dt_nullable = True else: field_dt = field.data_type dt_nullable = False # generate getter for field self.emit('@property') self.emit('def {}(self):'.format(field_name_reserved_check)) with self.indent(): self.emit('"""') if field.doc: self.emit_wrapped_text( self.process_doc(field.doc, self._docf)) # 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 self._{}_present:'.format(field_name)) with self.indent(): self.emit('return self._{}_value'.format(field_name)) self.emit('else:') with self.indent(): if dt_nullable: self.emit('return None') elif field.has_default: self.emit('return {}'.format( self._generate_python_value(ns, field.default))) else: self.emit( "raise AttributeError(\"missing required field '%s'\")" % field_name ) self.emit() # generate setter for field self.emit('@{}.setter'.format(field_name_reserved_check)) self.emit('def {}(self, val):'.format(field_name_reserved_check)) with self.indent(): if dt_nullable: self.emit('if val is None:') with self.indent(): self.emit('del self.{}'.format(field_name_reserved_check)) self.emit('return') if is_user_defined_type(field_dt): self.emit('self._%s_validator.validate_type_only(val)' % field_name) else: self.emit('val = self._{}_validator.validate(val)'.format(field_name)) self.emit('self._{}_value = val'.format(field_name)) self.emit('self._{}_present = True'.format(field_name)) self.emit() # generate deleter for field self.emit('@{}.deleter'.format(field_name_reserved_check)) self.emit('def {}(self):'.format(field_name_reserved_check)) with self.indent(): self.emit('self._{}_value = None'.format(field_name)) self.emit('self._{}_present = False'.format(field_name)) 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 _get_example_data(self, example_value, field): data_type, nullable = unwrap_nullable(field.data_type) field_name = fmt_var(field.name) result_args = [] if is_user_defined_type(data_type): obj_args = [] if is_union_type(data_type): for field in data_type.all_fields: if field.name == example_value['.tag']: if not is_void_type(field.data_type): if field.name in example_value: self._get_example_data( example_value[field.name], field) else: self._get_example_data(example_value, field) obj_args.append( (fmt_var(field.name), fmt_var(field.name))) field_value = fmt_func_call( caller=fmt_alloc_call(fmt_class_prefix(data_type)), callee='initWith{}'.format( fmt_camel_upper(example_value['.tag'])), args=fmt_func_args(obj_args)) self.emit('{} *{} = {};'.format(fmt_class_prefix(data_type), field_name, field_value)) else: if 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_value['.tag']: self._get_example_data(example_value, subtype) else: for field in data_type.all_fields: if field.name in example_value: obj_args.append((fmt_var(field.name), self._get_example_data( example_value[field.name], field.data_type))) else: if not is_void_type(field.data_type): obj_args.append( (fmt_var(field.name), self._fmt_default(field.data_type))) field_value = fmt_func_call( fmt_alloc_call(fmt_class_prefix(data_type)), 'initWith{}'.format( fmt_camel_upper(data_type.all_fields[0].name)), fmt_func_args(obj_args)) self.emit('{} *{} = {};'.format( fmt_class_prefix(data_type), field_name, field_value)) result_args.append((field_name, field_name)) elif is_list_type(data_type): if example_value: field_value = '@[{}]'.format( self._get_example_data(example_value[0], field)) else: field_value = 'nil' self.emit('NSArray *{} = {};'.format(field_name, field_value)) result_args.append((field_name, field_name)) elif is_numeric_type(data_type): if is_float_type(data_type): field_value = '[NSNumber numberWithDouble:{}]'.format( example_value) elif isinstance(data_type, (UInt64, Int64)): field_value = '[NSNumber numberWithLong:{}]'.format( example_value) else: field_value = '[NSNumber numberWithInt:{}]'.format( example_value) result_args.append((field_name, field_value)) elif is_timestamp_type(data_type): field_value = '[DbxNSDateSerializer deserialize:@"{}" dateFormat:@"{}"]'.format( example_value, data_type.format) self.emit('NSDate *{} = {};'.format(field_name, field_value)) result_args.append((field_name, field_name)) elif is_string_type(data_type): field_value = '@"{}"'.format(example_value) result_args.append((field_name, field_value)) elif is_boolean_type(data_type): field_value = '@YES' if bool(example_value) else '@NO' result_args.append((field_name, field_value)) return result_args