def _generate_routes(self, route_schema, namespace): for route in namespace.routes: var_name = fmt_func(route.name) data_types = [ route.arg_data_type, route.result_data_type, route.error_data_type ] with self.block('%s = bb.Route(' % var_name, delim=(None, None), after=')'): self.emit("'%s'," % route.name) self.emit("%r," % (route.deprecated is not None)) for data_type in data_types: self.emit( generate_validator_constructor(namespace, data_type) + ',') attrs = [] for field in route_schema.fields: attr_key = field.name attrs.append("'%s': %r" % (attr_key, route.attrs.get(attr_key))) self.generate_multiline_list(attrs, delim=('{', '}'), after=',', compact=True) if namespace.routes: self.emit() with self.block('ROUTES =', delim=('{', '}')): for route in namespace.routes: var_name = fmt_func(route.name) self.emit("'{}': {},".format(route.name, var_name)) self.emit()
def _generate_routes(self, route_schema, namespace): for route in namespace.routes: var_name = fmt_func(route.name) data_types = [route.arg_data_type, route.result_data_type, route.error_data_type] with self.block('%s = bb.Route(' % var_name, delim=(None, None), after=')'): self.emit("'%s'," % route.name) self.emit("%r," % (route.deprecated is not None)) for data_type in data_types: self.emit( generate_validator_constructor(namespace, data_type) + ',') attrs = [] for field in route_schema.fields: attr_key = field.name attrs.append("'%s': %r" % (attr_key, route.attrs.get(attr_key))) self.generate_multiline_list( attrs, delim=('{', '}'), after=',', compact=True) if namespace.routes: self.emit() with self.block('ROUTES =', delim=('{', '}')): for route in namespace.routes: var_name = fmt_func(route.name) self.emit("'{}': {},".format(route.name, var_name)) self.emit()
def _docf(self, tag, val): """ Callback used as the handler argument to process_docs(). This converts Babel doc references to Sphinx-friendly annotations. """ if tag == 'type': fq_val = val if '.' not in val: fq_val = self.cur_namespace.name + '.' + fq_val return ':class:`{}.{}`'.format(self.args.types_package, fq_val) elif tag == 'route': if ':' in val: val, version = val.split(':', 1) version = int(version) else: version = 1 if '.' in val: return ':meth:`{}`'.format(fmt_func(val, version=version)) else: return ':meth:`{}_{}`'.format( self.cur_namespace.name, fmt_func(val, version=version)) elif tag == 'link': anchor, link = val.rsplit(' ', 1) return '`{} <{}>`_'.format(anchor, link) elif tag == 'val': if val == 'null': return 'None' elif val == 'true' or val == 'false': return '``{}``'.format(val.capitalize()) else: return val elif tag == 'field': return '``{}``'.format(val) else: raise RuntimeError('Unknown doc ref tag %r' % tag)
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 _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 _generate_routes(self, route_schema, namespace): check_route_name_conflict(namespace) for route in namespace.routes: data_types = [route.arg_data_type, route.result_data_type, route.error_data_type] with self.block( '{} = bb.Route('.format(fmt_func(route.name, version=route.version)), delim=(None, None), after=')'): self.emit("'{}',".format(route.name)) self.emit('{},'.format(route.version)) self.emit('{!r},'.format(route.deprecated is not None)) for data_type in data_types: self.emit( generate_validator_constructor(namespace, data_type) + ',') attrs = [] for field in route_schema.fields: attr_key = field.name attrs.append("'{}': {!r}".format(attr_key, route.attrs.get(attr_key))) self.generate_multiline_list( attrs, delim=('{', '}'), after=',', compact=True) if namespace.routes: self.emit() with self.block('ROUTES =', delim=('{', '}')): for route in namespace.routes: self.emit("'{}': {},".format( route.name_with_version(), fmt_func(route.name, version=route.version))) self.emit()
def _docf(self, tag, val): """ Callback used as the handler argument to process_docs(). This converts Stone doc references to Sphinx-friendly annotations. """ if tag == 'type': return ':class:`{}`'.format(val) elif tag == 'route': if self.args.route_method: return ':meth:`%s`' % self.args.route_method.format( ns=self.cur_namespace.name, route=fmt_func(val)) else: return val elif tag == 'link': anchor, link = val.rsplit(' ', 1) return '`{} <{}>`_'.format(anchor, link) elif tag == 'val': if val == 'null': return 'None' elif val == 'true' or val == 'false': return '``{}``'.format(val.capitalize()) else: return val elif tag == 'field': return '``{}``'.format(val) else: raise RuntimeError('Unknown doc ref tag %r' % tag)
def _generate_union_class_variant_creators(self, ns, data_type): # type: (ApiNamespace, Union) -> None """ Generate the following section in the 'union Shape' example: @classmethod def circle(cls, val: float) -> Shape: ... """ union_type = class_name_for_data_type(data_type) for field in data_type.fields: if not is_void_type(field.data_type): field_name_reserved_check = fmt_func(field.name, check_reserved=True) val_type = self.map_stone_type_to_pep484_type( ns, field.data_type) self.emit('@classmethod') self.emit( 'def {field_name}(cls, val: {val_type}) -> {union_type}: ...' .format( field_name=field_name_reserved_check, val_type=val_type, union_type=union_type, )) self.emit()
def _generate_route_method_decl(self, namespace, route, arg_data_type, request_binary_body, method_name_suffix=None, extra_args=None): """Generates the method prototype for a route.""" method_name = fmt_func(route.name) namespace_name = fmt_func(namespace.name) if method_name_suffix: method_name += method_name_suffix args = ['self'] if extra_args: args += extra_args if request_binary_body: args.append('f') if is_struct_type(arg_data_type): for field in arg_data_type.all_fields: if is_nullable_type(field.data_type): args.append('{}=None'.format(field.name)) elif field.has_default: # TODO(kelkabany): Decide whether we really want to set the # default in the argument list. This will send the default # over the wire even if it isn't overridden. The benefit is # it locks in a default even if it is changed server-side. if is_user_defined_type(field.data_type): ns = field.data_type.namespace else: ns = None arg = '{}={}'.format( field.name, self._generate_python_value(ns, field.default)) args.append(arg) else: args.append(field.name) elif is_union_type(arg_data_type): args.append('arg') elif not is_void_type(arg_data_type): raise AssertionError('Unhandled request type: %r' % arg_data_type) self.generate_multiline_list( args, 'def {}_{}'.format(namespace_name, method_name), ':')
def _generate_routes( self, namespace, # type: ApiNamespace ): # type: (...) -> None for route in namespace.routes: var_name = fmt_func(route.name) self.emit("{var_name}: bb.Route = ...".format(var_name=var_name)) if namespace.routes: self.emit()
def _generate_union_class_is_set(self, data_type): for field in data_type.fields: field_name = fmt_func(field.name) self.emit('def is_{}(self):'.format(field_name)) with self.indent(): self.emit('"""') self.emit('Check if the union tag is ``%s``.' % field_name) self.emit() self.emit(':rtype: bool') self.emit('"""') self.emit("return self._tag == '{}'".format(field_name)) self.emit()
def _generate_route_method_decl( self, namespace, route, arg_data_type, request_binary_body, method_name_suffix=None, extra_args=None): """Generates the method prototype for a route.""" method_name = fmt_func(route.name) namespace_name = fmt_func(namespace.name) if method_name_suffix: method_name += method_name_suffix args = ['self'] if extra_args: args += extra_args if request_binary_body: args.append('f') if is_struct_type(arg_data_type): for field in arg_data_type.all_fields: if is_nullable_type(field.data_type): args.append('{}=None'.format(field.name)) elif field.has_default: # TODO(kelkabany): Decide whether we really want to set the # default in the argument list. This will send the default # over the wire even if it isn't overridden. The benefit is # it locks in a default even if it is changed server-side. if is_user_defined_type(field.data_type): ns = field.data_type.namespace else: ns = None arg = '{}={}'.format( field.name, self._generate_python_value(ns, field.default)) args.append(arg) else: args.append(field.name) elif is_union_type(arg_data_type): args.append('arg') elif not is_void_type(arg_data_type): raise AssertionError('Unhandled request type: %r' % arg_data_type) self.generate_multiline_list( args, 'def {}_{}'.format(namespace_name, method_name), ':')
def _generate_union_class_symbol_creators(self, data_type): """ Class attributes that represent a symbol are set after the union class definition. """ class_name = fmt_class(data_type.name) lineno = self.lineno for field in data_type.fields: if is_void_type(field.data_type): field_name = fmt_func(field.name) self.emit("{0}.{1} = {0}('{1}')".format(class_name, field_name)) if lineno != self.lineno: self.emit()
def _generate_struct_class_properties(self, ns, struct): # type: (ApiNamespace, Struct) -> None to_emit = [] # type: typing.List[typing.Text] for field in struct.all_fields: field_name_reserved_check = fmt_func(field.name, check_reserved=True) field_type = self.map_stone_type_to_pep484_type( ns, field.data_type) to_emit.append("{}: bb.Attribute[{}] = ...".format( field_name_reserved_check, field_type)) for s in to_emit: self.emit(s)
def _generate_routes( self, namespace, # type: ApiNamespace ): # type: (...) -> None check_route_name_conflict(namespace) for route in namespace.routes: self.emit("{method_name}: bb.Route = ...".format( method_name=fmt_func(route.name, version=route.version))) if namespace.routes: self.emit()
def _generate_routes( self, namespace, # type: ApiNamespace ): # type: (...) -> None check_route_name_conflict(namespace) for route in namespace.routes: self.emit( "{method_name}: bb.Route = ...".format( method_name=fmt_func(route.name, version=route.version))) if namespace.routes: self.emit()
def _generate_struct_class_properties(self, ns, struct): # type: (ApiNamespace, Struct) -> None to_emit = [] # type: typing.List[typing.Text] for field in struct.all_fields: field_name_reserved_check = fmt_func(field.name, check_reserved=True) field_type = self.map_stone_type_to_pep484_type( ns, field.data_type) to_emit.extend( self.property_template.format( field_name=field_name_reserved_check, field_type=field_type).split("\n")) for s in to_emit: self.emit(s)
def _generate_struct_class_properties(self, ns, struct): # type: (ApiNamespace, Struct) -> None to_emit = [] # type: typing.List[typing.Text] for field in struct.all_fields: field_name_reserved_check = fmt_func(field.name, check_reserved=True) field_type = self.map_stone_type_to_pep484_type(ns, field.data_type) to_emit.extend( self.property_template.format( field_name=field_name_reserved_check, field_type=field_type, ).split("\n") ) for s in to_emit: self.emit(s)
def _generate_union_class_get_helpers(self, ns, data_type): # type: (ApiNamespace, Union) -> None """ Generates the following section in the 'union Shape' example: def get_circle(self) -> float: ... """ for field in data_type.fields: field_name = fmt_func(field.name) if not is_void_type(field.data_type): # generate getter for field val_type = self.map_stone_type_to_pep484_type(ns, field.data_type) self.emit('def get_{field_name}(self) -> {val_type}: ...'.format( field_name=field_name, val_type=val_type, )) self.emit()
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) all_parent_fields = [ fmt_func(f.name, True) for f in data_type.parent_type.all_fields ] self.generate_multiline_list( all_parent_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_custom_annotations(self, ns, data_type): """ The _process_custom_annotations function allows client code to access custom annotations defined in the spec. """ self.emit( 'def _process_custom_annotations(self, annotation_type, field_path, processor):' ) with self.indent(), emit_pass_if_nothing_emitted(self): self.emit(( 'super({}, self)._process_custom_annotations(annotation_type, field_path, ' 'processor)').format(class_name_for_data_type(data_type))) self.emit() for field in data_type.fields: recursive_processors = list( self._generate_custom_annotation_processors( ns, field.data_type, field.custom_annotations)) # check if we have any annotations that apply to this field at all if len(recursive_processors) == 0: continue field_name = fmt_func(field.name) self.emit('if self.is_{}():'.format(field_name)) with self.indent(): for annotation_type, processor in recursive_processors: annotation_class = class_name_for_annotation_type( annotation_type, ns) self.emit('if annotation_type is {}:'.format( annotation_class)) with self.indent(): self.emit('self._value = {}'.format( generate_func_call( processor, args=[ "'{{}}.{}'.format(field_path)".format( field_name), 'self._value', ]))) self.emit()
def _generate_annotation_type_class_properties(self, ns, annotation_type): # type: (ApiNamespace, AnnotationType) -> None for param in annotation_type.params: param_name = fmt_var(param.name, True) prop_name = fmt_func(param.name, True) self.emit('@property') self.emit('def {}(self):'.format(prop_name)) with self.indent(): self.emit('"""') if param.doc: self.emit_wrapped_text( self.process_doc(param.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, param.data_type))) self.emit('"""') self.emit('return self._{}'.format(param_name)) 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) all_parent_fields = [fmt_func(f.name, True) for f in data_type.parent_type.all_fields] self.generate_multiline_list( all_parent_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, check_reserved=True) val_type = self.map_stone_type_to_pep484_type(ns, field.data_type) self.emit('@classmethod') self.emit('def {field_name}(cls, val: {val_type}) -> {union_type}: ...'.format( field_name=field_name_reserved_check, val_type=val_type, union_type=union_type, )) self.emit()
def _generate_struct_class_properties(self, ns, struct): # type: (ApiNamespace, Struct) -> None to_emit = [] # type: typing.List[typing.Text] for field in struct.all_fields: field_name_reserved_check = fmt_func(field.name, check_reserved=True) setter_field_type = self.map_stone_type_to_pep484_type(ns, field.data_type) # The field might not have been set since it is optional in the constructor. getter_field_type = setter_field_type if not is_nullable_type(field.data_type) and not is_void_type(field.data_type): self.import_tracker._register_typing_import('Optional') getter_field_type = 'Optional[{}]'.format(setter_field_type) to_emit.extend( self.property_template.format( field_name=field_name_reserved_check, getter_field_type=getter_field_type, setter_field_type=setter_field_type ).split("\n") ) for s in to_emit: self.emit(s)
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, check_reserved=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 args = '"{}"'.format(field_name) if dt_nullable: args += ", nullable=True" if is_user_defined_type(field_dt): args += ", user_defined=True" self.emit('# Instance attribute type: {} (validator is set below)'. format(self._python_type_mapping(ns, field_dt))) self.emit("{} = bb.Attribute({})".format(field_name, args)) self.emit()
def _generate_route_helper(self, namespace, route, download_to_file=False): """Generate a Python method that corresponds to a route. :param namespace: Namespace that the route belongs to. :param stone.ir.ApiRoute route: IR node for the route. :param bool download_to_file: Whether a special version of the route that downloads the response body to a file should be generated. This can only be used for download-style routes. """ arg_data_type = route.arg_data_type result_data_type = route.result_data_type request_binary_body = route.attrs.get('style') == 'upload' response_binary_body = route.attrs.get('style') == 'download' if download_to_file: assert response_binary_body, 'download_to_file can only be set ' \ 'for download-style routes.' self._generate_route_method_decl(namespace, route, arg_data_type, request_binary_body, method_name_suffix='_to_file', extra_args=['download_path']) else: self._generate_route_method_decl(namespace, route, arg_data_type, request_binary_body) with self.indent(): extra_request_args = None extra_return_arg = None footer = None if request_binary_body: extra_request_args = [('f', 'bytes', 'Contents to upload.')] elif download_to_file: extra_request_args = [('download_path', 'str', 'Path on local machine to save file.')] if response_binary_body and not download_to_file: extra_return_arg = ':class:`requests.models.Response`' footer = DOCSTRING_CLOSE_RESPONSE if route.doc: func_docstring = self.process_doc(route.doc, self._docf) else: func_docstring = None self._generate_docstring_for_func( namespace, arg_data_type, result_data_type, route.error_data_type, overview=func_docstring, extra_request_args=extra_request_args, extra_return_arg=extra_return_arg, footer=footer, ) self._maybe_generate_deprecation_warning(route) # Code to instantiate a class for the request data type if is_void_type(arg_data_type): self.emit('arg = None') elif is_struct_type(arg_data_type): self.generate_multiline_list( [f.name for f in arg_data_type.all_fields], before='arg = {}.{}'.format( fmt_namespace(arg_data_type.namespace.name), fmt_class(arg_data_type.name)), ) elif not is_union_type(arg_data_type): raise AssertionError('Unhandled request type %r' % arg_data_type) # Code to make the request args = [ '{}.{}'.format(fmt_namespace(namespace.name), fmt_func(route.name, version=route.version)), "'{}'".format(namespace.name), 'arg'] if request_binary_body: args.append('f') else: args.append('None') self.generate_multiline_list(args, 'r = self.request', compact=False) if download_to_file: self.emit('self._save_body_to_file(download_path, r[1])') if is_void_type(result_data_type): self.emit('return None') else: self.emit('return r[0]') else: if is_void_type(result_data_type): self.emit('return None') else: self.emit('return r') self.emit()
def _generate_union_class_is_set(self, union): # type: (Union) -> None for field in union.fields: field_name = fmt_func(field.name) self.emit('def is_{}(self) -> bool: ...'.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_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()