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_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 _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 _generate_union_class_variant_creators(self, ns, data_type): """ Each non-symbol, non-any variant has a corresponding class method that can be used to construct a union with that variant selected. """ for field in data_type.fields: if not is_void_type(field.data_type): field_name = fmt_func(field.name) field_name_reserved_check = fmt_func(field.name, True) if is_nullable_type(field.data_type): field_dt = field.data_type.data_type else: field_dt = field.data_type self.emit('@classmethod') self.emit('def {}(cls, val):'.format(field_name_reserved_check)) with self.indent(): self.emit('"""') self.emit_wrapped_text( 'Create an instance of this class set to the ``%s`` ' 'tag with value ``val``.' % field_name) self.emit() self.emit(':param {} val:'.format( self._python_type_mapping(ns, field_dt))) self.emit(':rtype: {}'.format( fmt_class(data_type.name))) self.emit('"""') self.emit("return cls('{}', val)".format(field_name)) self.emit()
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 _generate_union_class_variant_creators(self, ns, data_type): """ Each non-symbol, non-any variant has a corresponding class method that can be used to construct a union with that variant selected. """ for field in data_type.fields: if not is_void_type(field.data_type): field_name = fmt_func(field.name) field_name_reserved_check = fmt_func(field.name, True) if is_nullable_type(field.data_type): field_dt = field.data_type.data_type else: field_dt = field.data_type self.emit('@classmethod') self.emit('def {}(cls, val):'.format(field_name_reserved_check)) with self.indent(): self.emit('"""') self.emit_wrapped_text( 'Create an instance of this class set to the ``%s`` ' 'tag with value ``val``.' % field_name) self.emit() self.emit(':param {} val:'.format( self._python_type_mapping(ns, field_dt))) self.emit(':rtype: {}'.format( fmt_class(data_type.name))) self.emit('"""') self.emit("return cls('{}', val)".format(field_name)) self.emit()
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_field(self, field, union_field=False, namespace=None, raw=False): generate_doc(self, field) field_name = fmt_var(field.name) type_name = fmt_type(field.data_type, namespace, use_interface=True) json_tag = '`json:"%s"`' % field.name if is_nullable_type(field.data_type) or union_field: json_tag = '`json:"%s,omitempty"`' % field.name if raw: self.emit('%s json.RawMessage %s' % (field_name, json_tag)) else: self.emit('%s %s %s' % (field_name, type_name, json_tag))
def _generate_field(self, field, union_field=False, namespace=None, raw=False): generate_doc(self, field) field_name = fmt_var(field.name) type_name = fmt_type(field.data_type, namespace, use_interface=True) json_tag = '`json:"%s"`' % field.name if is_nullable_type(field.data_type) or union_field: json_tag = '`json:"%s,omitempty"`' % field.name if raw: self.emit('%s json.RawMessage %s' % (field_name, json_tag)) else: self.emit('%s %s %s' % (field_name, type_name, json_tag))
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 get_route_io_data_types(self): """ Returns a list of all user-defined data types that are referenced as either an argument, result, or error of a route. If a List or Nullable data type is referenced, then the contained data type is returned assuming it's a user-defined type. """ data_types = set() for route in self.routes: for dtype in (route.arg_data_type, route.result_data_type, route.error_data_type): while is_list_type(dtype) or is_nullable_type(dtype): dtype = dtype.data_type if is_composite_type(dtype) or is_alias(dtype): data_types.add(dtype) return sorted(data_types, key=lambda dt: dt.name)
def get_route_io_data_types(self): # type: () -> List[UserDefined] """ Returns a list of all user-defined data types that are referenced as either an argument, result, or error of a route. If a List or Nullable data type is referenced, then the contained data type is returned assuming it's a user-defined type. """ data_types = set() # type: Set[UserDefined] for route in self.routes: for dtype in (route.arg_data_type, route.result_data_type, route.error_data_type): while is_list_type(dtype) or is_nullable_type(dtype): dtype_as_list = cast(Union[DataTypeList, Nullable], dtype) dtype = dtype_as_list.data_type if is_composite_type(dtype) or is_alias(dtype): data_types.add(dtype) return sorted(data_types, key=lambda dt: dt.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 _determine_validator_type(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): item_validator = self._determine_validator_type(data_type.data_type) if item_validator: v = "arrayValidator({})".format( self._func_args([ ("minItems", data_type.min_items), ("maxItems", data_type.max_items), ("itemValidator", item_validator), ]) ) else: return None elif is_numeric_type(data_type): v = "comparableValidator({})".format( self._func_args([ ("minValue", data_type.min_value), ("maxValue", data_type.max_value), ]) ) elif is_string_type(data_type): pat = data_type.pattern if data_type.pattern else None if isinstance(pat, six.text_type): pat = pat.encode('unicode_escape') v = "stringValidator({})".format( self._func_args([ ("minLength", data_type.min_length), ("maxLength", data_type.max_length), ("pattern", '"{}"'.format(pat) if pat else None), ]) ) else: return None if nullable: v = "nullableValidator({})".format(v) return v
def format_type(typ): """Format a Stone data type as a TypeScript type""" # TODO - Where the type is an alias, emit the alias name and then emit # `type $alias = $wrapped;` """Return a TypeScript representation of a stone DataType""" if data_type.is_nullable_type(typ): wrapped_type, _ = data_type.unwrap_nullable(typ) # If the type is nullable, assume that it has been marked as an # optional member of the containing struct, ie. "field?: type", so we # don't need to represent the fact that it might be null in the # formatted type. ie. There is no need to generate "field?: type | # null" return '{}'.format(format_type(wrapped_type)) if data_type.is_primitive_type(typ): return _primitive_type_map.get(typ.name) elif data_type.is_list_type(typ): return '{}[]'.format(format_type(typ.data_type)) else: return typ.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 _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 _struct_init_args(self, data_type, namespace=None): args = [] for field in data_type.all_fields: name = fmt_var(field.name) value = self._swift_type_mapping(field.data_type) field_type = field.data_type if is_nullable_type(field_type): field_type = field_type.data_type nullable = True else: nullable = False if field.has_default: if is_union_type(field_type): default = '.{}'.format(fmt_class(field.default.tag_name)) else: default = fmt_obj(field.default) value += ' = {}'.format(default) elif nullable: value += ' = nil' arg = (name, value) args.append(arg) return args
def _generate_union_class_get_helpers(self, ns, data_type): """ These are the getters used to access the value of a variant, once the tag has been switched on. """ for field in data_type.fields: field_name = fmt_func(field.name) if not is_void_type(field.data_type): # generate getter for field self.emit('def get_{}(self):'.format(field_name)) with self.indent(): if is_nullable_type(field.data_type): field_dt = field.data_type.data_type else: field_dt = field.data_type self.emit('"""') if field.doc: self.emit_wrapped_text( self.process_doc(field.doc, self._docf)) self.emit() self.emit("Only call this if :meth:`is_%s` is true." % field_name) # Sphinx wants an extra line between the text and the # rtype declaration. self.emit() self.emit(':rtype: {}'.format( self._python_type_mapping(ns, field_dt))) self.emit('"""') self.emit('if not self.is_{}():'.format(field_name)) with self.indent(): self.emit( 'raise AttributeError("tag \'{}\' not set")'.format( field_name)) self.emit('return self._value') self.emit()
def _generate_union_class_get_helpers(self, ns, data_type): """ These are the getters used to access the value of a variant, once the tag has been switched on. """ for field in data_type.fields: field_name = fmt_func(field.name) if not is_void_type(field.data_type): # generate getter for field self.emit('def get_{}(self):'.format(field_name)) with self.indent(): if is_nullable_type(field.data_type): field_dt = field.data_type.data_type else: field_dt = field.data_type self.emit('"""') if field.doc: self.emit_wrapped_text( self.process_doc(field.doc, self._docf)) self.emit() self.emit("Only call this if :meth:`is_%s` is true." % field_name) # Sphinx wants an extra line between the text and the # rtype declaration. self.emit() self.emit(':rtype: {}'.format( self._python_type_mapping(ns, field_dt))) self.emit('"""') self.emit('if not self.is_{}():'.format(field_name)) with self.indent(): self.emit( 'raise AttributeError("tag \'{}\' not set")'.format( field_name)) self.emit('return self._value') self.emit()
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 _struct_has_defaults(self, struct): """Returns whether the given struct has any default values.""" return [ f for f in struct.all_fields if f.has_default or is_nullable_type(f.data_type) ]
def _struct_has_defaults(self, struct): """Returns whether the given struct has any default values.""" return [f for f in struct.all_fields if f.has_default or is_nullable_type(f.data_type)]