def _generate_struct_class_repr(self, data_type): """ Generates something like: def __repr__(self): return 'Employee(first_name={!r}, last_name={!r}, age={!r})'.format( self._first_name_value, self._last_name_value, self._age_value, ) """ self.emit('def __repr__(self):') with self.indent(): if data_type.all_fields: constructor_kwargs_fmt = ', '.join( '{}={{!r}}'.format(fmt_var(f.name, True)) for f in data_type.all_fields) self.emit("return '{}({})'.format(".format( class_name_for_data_type(data_type), constructor_kwargs_fmt, )) with self.indent(): for f in data_type.all_fields: self.emit("self._{}_value,".format(fmt_var(f.name))) self.emit(")") else: self.emit("return '%s()'" % class_name_for_data_type(data_type)) self.emit()
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 _generate_union_class_reflection_attributes(self, ns, data_type): """ Adds a class attribute for each union member assigned to a validator. Also adds an attribute that is a map from tag names to validators. """ class_name = fmt_class(data_type.name) for field in data_type.fields: field_name = fmt_var(field.name) validator_name = generate_validator_constructor( ns, field.data_type) self.emit('{}._{}_validator = {}'.format( class_name, field_name, validator_name)) with self.block('{}._tagmap ='.format(class_name)): for field in data_type.fields: var_name = fmt_var(field.name) validator_name = '{}._{}_validator'.format( class_name, var_name) self.emit("'{}': {},".format(var_name, validator_name)) if data_type.parent_type: self.emit('{0}._tagmap.update({1}._tagmap)'.format( class_name, class_name_for_data_type(data_type.parent_type, ns))) 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_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_python_value(self, ns, value): if is_tag_ref(value): ref = '{}.{}'.format( class_name_for_data_type(value.union_data_type), fmt_var(value.tag_name)) if ns != value.union_data_type.namespace: ref = '%s.%s' % (value.union_data_type.namespace.name, ref) return ref else: return fmt_obj(value)
def _generate_union_class_repr(self, data_type): """ The __repr__() function will return a string of the class name, and the selected tag. """ self.emit('def __repr__(self):') with self.indent(): self.emit("return '{}(%r, %r)' % (self._tag, self._value)".format( class_name_for_data_type(data_type), )) self.emit()
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 _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_vars(self, ns, data_type): # type: (ApiNamespace, Union) -> None lineno = self.lineno # Generate stubs for class variables so that IDEs like PyCharms have an # easier time detecting their existence. for field in data_type.fields: if is_void_type(field.data_type): field_name = fmt_var(field.name) field_type = class_name_for_data_type(data_type, ns) self.emit('{field_name} = ... # type: {field_type}'.format( field_name=field_name, field_type=field_type, )) if lineno != self.lineno: self.emit()
def _generate_struct_class_init(self, data_type): """ Generates constructor. The constructor takes all possible fields as optional arguments. Any argument that is set on construction sets the corresponding field for the instance. """ args = ['self'] for field in data_type.all_fields: field_name_reserved_check = fmt_var(field.name, True) args.append('%s=None' % field_name_reserved_check) self.generate_multiline_list(args, before='def __init__', after=':') with self.indent(): lineno = self.lineno # Call the parent constructor if a super type exists if data_type.parent_type: class_name = class_name_for_data_type(data_type) self.generate_multiline_list( [ fmt_func(f.name, True) for f in data_type.parent_type.all_fields ], before='super({}, self).__init__'.format(class_name)) # initialize each field for field in data_type.fields: field_var_name = fmt_var(field.name) self.emit('self._{}_value = None'.format(field_var_name)) self.emit('self._{}_present = False'.format(field_var_name)) # handle arguments that were set for field in data_type.fields: field_var_name = fmt_var(field.name, True) self.emit('if {} is not None:'.format(field_var_name)) with self.indent(): self.emit('self.{0} = {0}'.format(field_var_name)) if lineno == self.lineno: self.emit('pass') self.emit()
def _generate_union_class_variant_creators(self, ns, data_type): # type: (ApiNamespace, Union) -> None """ Generate the following section in the 'union Shape' example: @classmethod def circle(cls, val: float) -> Shape: ... """ union_type = class_name_for_data_type(data_type) for field in data_type.fields: if not is_void_type(field.data_type): field_name_reserved_check = fmt_func(field.name, True) val_type = self.map_stone_type_to_pep484_type(ns, field.data_type) self.emit('@classmethod') self.emit('def {field_name}(cls, val: {val_type}) -> {union_type}: ...'.format( field_name=field_name_reserved_check, val_type=val_type, union_type=union_type, )) self.emit()
def _generate_struct_class_init(self, data_type): """ Generates constructor. The constructor takes all possible fields as optional arguments. Any argument that is set on construction sets the corresponding field for the instance. """ args = ['self'] for field in data_type.all_fields: field_name_reserved_check = fmt_var(field.name, True) args.append('%s=None' % field_name_reserved_check) self.generate_multiline_list(args, before='def __init__', after=':') with self.indent(): lineno = self.lineno # Call the parent constructor if a super type exists if data_type.parent_type: class_name = class_name_for_data_type(data_type) self.generate_multiline_list( [fmt_func(f.name, True) for f in data_type.parent_type.all_fields], before='super({}, self).__init__'.format(class_name)) # initialize each field for field in data_type.fields: field_var_name = fmt_var(field.name) self.emit('self._{}_value = None'.format(field_var_name)) self.emit('self._{}_present = False'.format(field_var_name)) # handle arguments that were set for field in data_type.fields: field_var_name = fmt_var(field.name, True) self.emit('if {} is not None:'.format(field_var_name)) with self.indent(): self.emit('self.{0} = {0}'.format(field_var_name)) if lineno == self.lineno: self.emit('pass') self.emit()
def _generate_struct_class(self, ns, data_type): # type: (ApiNamespace, Struct) -> None """Defines a Python class that represents a struct in Stone.""" self.emit(self._class_declaration_for_type(ns, data_type)) with self.indent(): if data_type.has_documented_type_or_fields(): self.emit('"""') if data_type.doc: self.emit_wrapped_text( self.process_doc(data_type.doc, self._docf)) if data_type.has_documented_fields(): self.emit() for field in data_type.fields: if not field.doc: continue self.emit_wrapped_text(':ivar {}: {}'.format( fmt_var(field.name), self.process_doc(field.doc, self._docf)), subsequent_prefix=' ') self.emit('"""') self.emit() self._generate_struct_class_slots(data_type) self._generate_struct_class_has_required_fields(data_type) self._generate_struct_class_init(data_type) self._generate_struct_class_properties(ns, data_type) self._generate_struct_class_repr(data_type) if data_type.has_enumerated_subtypes(): validator = 'StructTree' else: validator = 'Struct' self.emit('{0}_validator = bv.{1}({0})'.format( class_name_for_data_type(data_type), validator, )) self.emit()
def _generate_struct_class_reflection_attributes(self, ns, data_type): """ Generates two class attributes: * _all_field_names_: Set of all field names including inherited fields. * _all_fields_: List of tuples, where each tuple is (name, validator). If a struct has enumerated subtypes, then two additional attributes are generated: * _field_names_: Set of all field names excluding inherited fields. * _fields: List of tuples, where each tuple is (name, validator), and excludes inherited fields. These are needed because serializing a struct with enumerated subtypes requires knowing the fields defined in each level of the hierarchy. """ class_name = class_name_for_data_type(data_type) if data_type.parent_type: parent_type_class_name = class_name_for_data_type( data_type.parent_type, ns) else: parent_type_class_name = None for field in data_type.fields: field_name = fmt_var(field.name) validator_name = generate_validator_constructor(ns, field.data_type) self.emit('{}._{}_validator = {}'.format( class_name, field_name, validator_name)) if data_type.is_member_of_enumerated_subtypes_tree(): self.generate_multiline_list( ["'%s'" % field.name for field in data_type.fields], before='{}._field_names_ = set('.format(class_name), after=')', delim=('[', ']'), compact=False) if parent_type_class_name: self.emit( '{0}._all_field_names_ = ' '{1}._all_field_names_.union({0}._field_names_)' .format(class_name, parent_type_class_name)) else: self.emit('{0}._all_field_names_ = {0}._field_names_'.format( class_name)) else: if parent_type_class_name: before = ( '{}._all_field_names_ = ' '{}._all_field_names_.union(set(').format( class_name, parent_type_class_name) after = '))' else: before = '{}._all_field_names_ = set('.format(class_name) after = ')' self.generate_multiline_list( ["'%s'" % field.name for field in data_type.fields], before=before, after=after, delim=('[', ']'), compact=False) if data_type.is_member_of_enumerated_subtypes_tree(): items = [] for field in data_type.fields: var_name = fmt_var(field.name) validator_name = '{}._{}_validator'.format(class_name, var_name) items.append("('{}', {})".format(var_name, validator_name)) self.generate_multiline_list( items, before='{}._fields_ = '.format(class_name), delim=('[', ']'), compact=False, ) if parent_type_class_name: self.emit( '{0}._all_fields_ = ' '{1}._all_fields_ + {0}._fields_'.format( class_name, parent_type_class_name)) else: self.emit('{0}._all_fields_ = {0}._fields_'.format(class_name)) else: if parent_type_class_name: before = '{}._all_fields_ = {}._all_fields_ + '.format( class_name, parent_type_class_name) else: before = '{}._all_fields_ = '.format(class_name) items = [] for field in data_type.fields: var_name = fmt_var(field.name) validator_name = '{}._{}_validator'.format( class_name, var_name) items.append("('{}', {})".format(var_name, validator_name)) self.generate_multiline_list( items, before=before, delim=('[', ']'), compact=False) self.emit()