def _load_enum( self, enum: descriptor_pb2.EnumDescriptorProto, address: metadata.Address, path: Tuple[int], ) -> wrappers.EnumType: """Load enum descriptions from EnumDescriptorProtos.""" address = address.child(enum.name, path) # Put together wrapped objects for the enum values. values = [] for enum_value, i in zip(enum.value, range(0, sys.maxsize)): values.append( wrappers.EnumValueType( enum_value_pb=enum_value, meta=metadata.Metadata( address=address, documentation=self.docs.get(path + (2, i), self.EMPTY), ), )) # Load the enum itself. self.proto_enums[address.proto] = wrappers.EnumType( enum_pb=enum, meta=metadata.Metadata( address=address, documentation=self.docs.get(path, self.EMPTY), ), values=values, ) return self.proto_enums[address.proto]
def _load_enum(self, enum: descriptor_pb2.EnumDescriptorProto, address: metadata.Address, path: Tuple[int]) -> None: """Load enum descriptions from EnumDescriptorProtos.""" # Put together wrapped objects for the enum values. values = [] for enum_value, i in zip(enum.value, range(0, sys.maxsize)): values.append( wrappers.EnumValueType( enum_value_pb=enum_value, meta=metadata.Metadata( address=address, documentation=self.docs.get(path + (2, i), self.EMPTY), ), )) # Load the enum itself. ident = f'{str(address)}.{enum.name}' self.enums[ident] = wrappers.EnumType( enum_pb=enum, meta=metadata.Metadata( address=address, documentation=self.docs.get(path, self.EMPTY), ), values=values, )
def _client_output(self, enable_asyncio: bool): """Return the output from the client layer. This takes into account transformations made by the outer GAPIC client to transform the output from the transport. Returns: Union[~.MessageType, ~.PythonType]: A description of the return type. """ # Void messages ultimately return None. if self.void: return PrimitiveType.build(None) # If this method is an LRO, return a PythonType instance representing # that. if self.lro: return PythonType(meta=metadata.Metadata( address=metadata.Address( name='AsyncOperation' if enable_asyncio else 'Operation', module='operation_async' if enable_asyncio else 'operation', package=('google', 'api_core'), collisions=self.lro.response_type.ident.collisions, ), documentation=utils.doc( 'An object representing a long-running operation. \n\n' 'The result type for the operation will be ' ':class:`{ident}` {doc}'.format( doc=self.lro.response_type.meta.doc, ident=self.lro.response_type.ident.sphinx, ), ), )) # If this method is paginated, return that method's pager class. if self.paged_result_field: return PythonType(meta=metadata.Metadata( address=metadata.Address( name=f'{self.name}AsyncPager' if enable_asyncio else f'{self.name}Pager', package=self.ident.api_naming.module_namespace + (self.ident.api_naming.versioned_module_name, ) + self.ident.subpackage + ( 'services', utils.to_snake_case(self.ident.parent[-1]), ), module='pagers', collisions=self.input.ident.collisions, ), documentation=utils.doc( f'{self.output.meta.doc}\n\n' 'Iterating over this object will yield results and ' 'resolve additional pages automatically.', ), )) # Return the usual output. return self.output
def make_method( name: str, input_message: wrappers.MessageType = None, output_message: wrappers.MessageType = None, package: str = 'foo.bar.v1', module: str = 'baz', **kwargs) -> wrappers.Method: # Use default input and output messages if they are not provided. input_message = input_message or make_message('MethodInput') output_message = output_message or make_message('MethodOutput') # Create the method pb2. method_pb = descriptor_pb2.MethodDescriptorProto( name=name, input_type=str(input_message.meta.address), output_type=str(output_message.meta.address), ) # Instantiate the wrapper class. return wrappers.Method( method_pb=method_pb, input=input_message, output=output_message, meta=metadata.Metadata(address=metadata.Address( package=package, module=module, )), )
def make_field(name: str = 'my_field', number: int = 1, repeated: bool = False, message: wrappers.MessageType = None, enum: wrappers.EnumType = None, meta: metadata.Metadata = None, **kwargs) -> wrappers.Field: T = desc.FieldDescriptorProto.Type if message: kwargs.setdefault('type_name', str(message.meta.address)) kwargs['type'] = 'TYPE_MESSAGE' elif enum: kwargs.setdefault('type_name', str(enum.meta.address)) kwargs['type'] = 'TYPE_ENUM' else: kwargs.setdefault('type', T.Value('TYPE_BOOL')) if isinstance(kwargs['type'], str): kwargs['type'] = T.Value(kwargs['type']) label = kwargs.pop('label', 3 if repeated else 1) field_pb = desc.FieldDescriptorProto(name=name, label=label, number=number, **kwargs) return wrappers.Field( field_pb=field_pb, enum=enum, message=message, meta=meta or metadata.Metadata(), )
def _load_service( self, service: descriptor_pb2.ServiceDescriptorProto, address: metadata.Address, path: Tuple[int], ) -> wrappers.Service: """Load comments for a service and its methods.""" address = address.child(service.name, path) # Put together a dictionary of the service's methods. methods = self._get_methods( service.method, service_address=address, path=path + (2, ), ) # Load the comments for the service itself. self.proto_services[address.proto] = wrappers.Service( meta=metadata.Metadata( address=address, documentation=self.docs.get(path, self.EMPTY), ), methods=methods, service_pb=service, ) return self.proto_services[address.proto]
def make_enum( name: str, package: str = 'foo.bar.v1', module: str = 'baz', values: typing.Tuple[str, int] = (), meta: metadata.Metadata = None, ) -> wrappers.EnumType: enum_value_pbs = [ desc.EnumValueDescriptorProto(name=i[0], number=i[1]) for i in values ] enum_pb = desc.EnumDescriptorProto( name=name, value=enum_value_pbs, ) return wrappers.EnumType( enum_pb=enum_pb, values=[ wrappers.EnumValueType(enum_value_pb=evpb) for evpb in enum_value_pbs ], meta=meta or metadata.Metadata(address=metadata.Address( name=name, package=tuple(package.split('.')), module=module, )), )
def proto(self) -> Proto: """Return a Proto dataclass object.""" # Create a "context-naïve" proto. # This has everything but is ignorant of naming collisions in the # ultimate file that will be written. naive = Proto( all_enums=self.proto_enums, all_messages=self.proto_messages, file_pb2=self.file_descriptor, file_to_generate=self.file_to_generate, services=self.proto_services, meta=metadata.Metadata(address=self.address, ), ) # If this is not a file being generated, we do not need to # do anything else. if not self.file_to_generate: return naive # Return a context-aware proto object. return dataclasses.replace( naive, all_enums=collections.OrderedDict( (k, v.with_context(collisions=naive.names)) for k, v in naive.all_enums.items()), all_messages=collections.OrderedDict( (k, v.with_context(collisions=naive.names)) for k, v in naive.all_messages.items()), services=collections.OrderedDict( # Note: services bind to themselves because services get their # own output files. (k, v.with_context(collisions=v.names)) for k, v in naive.services.items()), meta=naive.meta.with_context(collisions=naive.names), )
def get_message( dot_path: str, *, fields: typing.Tuple[desc.FieldDescriptorProto] = (), ) -> wrappers.MessageType: # Pass explicit None through (for lro_metadata). if dot_path is None: return None # Note: The `dot_path` here is distinct from the canonical proto path # because it includes the module, which the proto path does not. # # So, if trying to test the DescriptorProto message here, the path # would be google.protobuf.descriptor.DescriptorProto (whereas the proto # path is just google.protobuf.DescriptorProto). pieces = dot_path.split('.') pkg, module, name = pieces[:-2], pieces[-2], pieces[-1] return wrappers.MessageType( fields={ i.name: wrappers.Field( field_pb=i, enum=get_enum(i.type_name) if i.type_name else None, ) for i in fields }, nested_messages={}, nested_enums={}, message_pb=desc.DescriptorProto(name=name, field=fields), meta=metadata.Metadata(address=metadata.Address( name=name, package=tuple(pkg), module=module, )), )
def test_operation_meta(): lro_response = wrappers.MessageType( fields={}, message_pb=descriptor_pb2.DescriptorProto(name='LroResponse'), meta=metadata.Metadata(address=metadata.Address(module='foo')), ) operation = wrappers.OperationType(lro_response=lro_response) assert 'representing a long-running operation' in operation.meta.doc assert ':class:`~.foo_pb2.LroResponse`' in operation.meta.doc
def test_message_docstring(): L = descriptor_pb2.SourceCodeInfo.Location meta = metadata.Metadata(documentation=L( leading_comments='Lorem ipsum', trailing_comments='dolor set amet', )) message = make_message('Name', meta=meta) assert message.meta.doc == 'Lorem ipsum'
def _load_message( self, message_pb: descriptor_pb2.DescriptorProto, address: metadata.Address, path: Tuple[int], ) -> wrappers.MessageType: """Load message descriptions from DescriptorProtos.""" address = address.child(message_pb.name, path) # Load all nested items. # # Note: This occurs before piecing together this message's fields # because if nested types are present, they are generally the # type of one of this message's fields, and they need to be in # the registry for the field's message or enum attributes to be # set correctly. nested_enums = self._load_children( message_pb.enum_type, address=address, loader=self._load_enum, path=path + (4, ), ) nested_messages = self._load_children( message_pb.nested_type, address=address, loader=self._load_message, path=path + (3, ), ) # self._load_children(message.oneof_decl, loader=self._load_field, # address=nested_addr, info=info.get(8, {})) # Create a dictionary of all the fields for this message. fields = self._get_fields( message_pb.field, address=address, path=path + (2, ), ) fields.update( self._get_fields( message_pb.extension, address=address, path=path + (6, ), )) # Create a message correspoding to this descriptor. self.proto_messages[address.proto] = wrappers.MessageType( fields=fields, message_pb=message_pb, nested_enums=nested_enums, nested_messages=nested_messages, meta=metadata.Metadata( address=address, documentation=self.docs.get(path, self.EMPTY), ), ) return self.proto_messages[address.proto]
def make_method( name: str, input_message: wrappers.MessageType = None, output_message: wrappers.MessageType = None, package: typing.Union[typing.Tuple[str], str] = 'foo.bar.v1', module: str = 'baz', http_rule: http_pb2.HttpRule = None, signatures: typing.Sequence[str] = (), is_deprecated: bool = False, routing_rule: routing_pb2.RoutingRule = None, **kwargs ) -> wrappers.Method: # Use default input and output messages if they are not provided. input_message = input_message or make_message('MethodInput') output_message = output_message or make_message('MethodOutput') # Create the method pb2. method_pb = desc.MethodDescriptorProto( name=name, input_type=str(input_message.meta.address), output_type=str(output_message.meta.address), **kwargs ) if routing_rule: ext_key = routing_pb2.routing method_pb.options.Extensions[ext_key].MergeFrom(routing_rule) # If there is an HTTP rule, process it. if http_rule: ext_key = annotations_pb2.http method_pb.options.Extensions[ext_key].MergeFrom(http_rule) # If there are signatures, include them. for sig in signatures: ext_key = client_pb2.method_signature method_pb.options.Extensions[ext_key].append(sig) if isinstance(package, str): package = tuple(package.split('.')) if is_deprecated: method_pb.options.deprecated = True # Instantiate the wrapper class. return wrappers.Method( method_pb=method_pb, input=input_message, output=output_message, meta=metadata.Metadata(address=metadata.Address( name=name, package=package, module=module, parent=(f'{name}Service',), )), )
def _get_methods( self, methods: Sequence[descriptor_pb2.MethodDescriptorProto], address: metadata.Address, path: Tuple[int, ...], ) -> Mapping[str, wrappers.Method]: """Return a dictionary of wrapped methods for the given service. Args: methods (Sequence[~.descriptor_pb2.MethodDescriptorProto]): A sequence of protobuf method objects. address (~.metadata.Address): An address object denoting the location of these methods. path (Tuple[int]): The source location path thus far, as understood by ``SourceCodeInfo.Location``. Returns: Mapping[str, ~.wrappers.Method]: A ordered mapping of :class:`~.wrappers.Method` objects. """ # Iterate over the methods and collect them into a dictionary. answer: Dict[str, wrappers.Method] = collections.OrderedDict() for meth_pb, i in zip(methods, range(0, sys.maxsize)): lro = None # If the output type is google.longrunning.Operation, we use # a specialized object in its place. if meth_pb.output_type.endswith('google.longrunning.Operation'): op = meth_pb.options.Extensions[operations_pb2.operation_info] if not op.response_type or not op.metadata_type: raise TypeError( f'rpc {meth_pb.name} returns a google.longrunning.' 'Operation, but is missing a response type or ' 'metadata type.', ) lro = wrappers.OperationInfo( response_type=self.api_messages[address.resolve( op.response_type, )], metadata_type=self.api_messages[address.resolve( op.metadata_type, )], ) # Create the method wrapper object. answer[meth_pb.name] = wrappers.Method( input=self.api_messages[meth_pb.input_type.lstrip('.')], lro=lro, method_pb=meth_pb, meta=metadata.Metadata( address=address.child(meth_pb.name, path + (i, )), documentation=self.docs.get(path + (i, ), self.EMPTY), ), output=self.api_messages[meth_pb.output_type.lstrip('.')], ) # Done; return the answer. return answer
def make_doc_meta( *, leading: str = '', trailing: str = '', detached: typing.List[str] = [], ) -> desc.SourceCodeInfo.Location: return metadata.Metadata(documentation=desc.SourceCodeInfo.Location( leading_comments=leading, trailing_comments=trailing, leading_detached_comments=detached, ), )
def make_field(name: str, repeated: bool = False, meta: metadata.Metadata = None, **kwargs) -> wrappers.Method: field_pb = descriptor_pb2.FieldDescriptorProto( name=name, label=3 if repeated else 1, **kwargs ) return wrappers.Field( field_pb=field_pb, meta=meta or metadata.Metadata(), )
def test_message_pb2_sphinx_ident(): meta = metadata.Metadata( address=metadata.Address(name='Timestamp', package=('google', 'protobuf'), module='timestamp', api_naming=naming.NewNaming( proto_package="foo.bar"))) message = make_message("Timestamp", package='google.protobuf', module='timestamp', meta=meta) assert message.ident.sphinx == 'google.protobuf.timestamp_pb2.Timestamp'
def get_enum(dot_path: str) -> wrappers.EnumType: pieces = dot_path.split('.') pkg, module, name = pieces[:-2], pieces[-2], pieces[-1] return wrappers.EnumType( enum_pb=desc.EnumDescriptorProto(name=name), meta=metadata.Metadata(address=metadata.Address( name=name, package=tuple(pkg), module=module, )), values=[], )
def _get_fields( self, field_pbs: Sequence[descriptor_pb2.FieldDescriptorProto], address: metadata.Address, path: Tuple[int, ...], oneofs: Optional[Dict[str, wrappers.Oneof]] = None ) -> Dict[str, wrappers.Field]: """Return a dictionary of wrapped fields for the given message. Args: field_pbs (Sequence[~.descriptor_pb2.FieldDescriptorProto]): A sequence of protobuf field objects. address (~.metadata.Address): An address object denoting the location of these fields. path (Tuple[int]): The source location path thus far, as understood by ``SourceCodeInfo.Location``. Returns: Mapping[str, ~.wrappers.Field]: A ordered mapping of :class:`~.wrappers.Field` objects. """ # Iterate over the fields and collect them into a dictionary. # # The saving of the enum and message types rely on protocol buffers' # naming rules to trust that they will never collide. # # Note: If this field is a recursive reference to its own message, # then the message will not be in `api_messages` yet (because the # message wrapper is not yet created, because it needs this object # first) and this will be None. This case is addressed in the # `_load_message` method. answer: Dict[str, wrappers.Field] = collections.OrderedDict() for i, field_pb in enumerate(field_pbs): is_oneof = oneofs and field_pb.HasField('oneof_index') oneof_name = nth((oneofs or {}).keys(), field_pb.oneof_index) if is_oneof else None field = wrappers.Field( field_pb=field_pb, enum=self.api_enums.get(field_pb.type_name.lstrip('.')), message=self.api_messages.get(field_pb.type_name.lstrip('.')), meta=metadata.Metadata( address=address.child(field_pb.name, path + (i, )), documentation=self.docs.get(path + (i, ), self.EMPTY), ), oneof=oneof_name, ) answer[field.name] = field # Done; return the answer. return answer
def test_mock_value_original_type_message(): subfields = collections.OrderedDict(( ('foo', make_field(name='foo', type='TYPE_INT32')), ('bar', make_field(name='bar', type='TYPE_STRING')) )) message = wrappers.MessageType( fields=subfields, message_pb=descriptor_pb2.DescriptorProto(name='Message', field=[ i.field_pb for i in subfields.values() ]), meta=metadata.Metadata(address=metadata.Address( module='bogus', name='Message', )), nested_enums={}, nested_messages={}, ) field = make_field( type='TYPE_MESSAGE', type_name='bogus.Message', message=message, ) mock = field.mock_value_original_type assert mock == {"foo": 324, "bar": "bar_value"} # Messages by definition aren't primitive with pytest.raises(TypeError): field.primitive_mock() # Special case for map entries entry_msg = make_message( name='MessageEntry', fields=( make_field(name='key', type='TYPE_STRING'), make_field(name='value', type='TYPE_STRING'), ), options=descriptor_pb2.MessageOptions(map_entry=True), ) entry_field = make_field( name="messages", type_name="stuff.MessageEntry", message=entry_msg, label=3, type='TYPE_MESSAGE', ) assert entry_field.mock_value_original_type == {}
def __init__(self, *, fields={}, type="", options=False, ident=False, resource_path=False, meta=None): self.fields = fields self.type = type self.options = options self.ident = ident self.resource_path = resource_path self.meta = meta or metadata.Metadata()
def _get_methods( self, methods: List[descriptor_pb2.MethodDescriptorProto], address: metadata.Address, path: Tuple[int], ) -> Mapping[str, wrappers.Method]: """Return a dictionary of wrapped methods for the given service. Args: methods (Sequence[~.descriptor_pb2.MethodDescriptorProto]): A sequence of protobuf method objects. address (~.metadata.Address): An address object denoting the location of these methods. path (Tuple[int]): The source location path thus far, as understood by ``SourceCodeInfo.Location``. Returns: Mapping[str, ~.wrappers.Method]: A ordered mapping of :class:`~.wrappers.Method` objects. """ # Iterate over the methods and collect them into a dictionary. answer = collections.OrderedDict() for meth_pb, i in zip(methods, range(0, sys.maxsize)): types = meth_pb.options.Extensions[operations_pb2.operation_types] # If the output type is google.longrunning.Operation, we use # a specialized object in its place. output_type = self.all_messages[meth_pb.output_type.lstrip('.')] if meth_pb.output_type.endswith('google.longrunning.Operation'): output_type = self._get_operation_type( response_type=self.all_messages[address.resolve( types.response)], metadata_type=self.all_messages.get( address.resolve(types.metadata), ), ) # Create the method wrapper object. answer[meth_pb.name] = wrappers.Method( input=self.all_messages[meth_pb.input_type.lstrip('.')], method_pb=meth_pb, meta=metadata.Metadata( address=address, documentation=self.docs.get(path + (i, ), self.EMPTY), ), output=output_type, ) # Done; return the answer. return answer
def make_field(name: str, repeated: bool = False, message: wrappers.MessageType = None, meta: metadata.Metadata = None, **kwargs) -> wrappers.Method: if message: kwargs['type_name'] = str(message.meta.address) field_pb = descriptor_pb2.FieldDescriptorProto(name=name, label=3 if repeated else 1, **kwargs) return wrappers.Field( field_pb=field_pb, message=message, meta=meta or metadata.Metadata(), )
def make_message(name: str, package: str = 'foo.bar.v1', module: str = 'baz', fields: Sequence[wrappers.Field] = (), ) -> wrappers.MessageType: message_pb = descriptor_pb2.DescriptorProto( name=name, field=[i.field_pb for i in fields], ) return wrappers.MessageType( message_pb=message_pb, fields=collections.OrderedDict((i.name, i) for i in fields), meta=metadata.Metadata(address=metadata.Address( package=tuple(package.split('.')), module=module, )), )
def build(cls, primitive_type: Optional[type]): """Return a PrimitiveType object for the given Python primitive type. Args: primitive_type (cls): A Python primitive type, such as :class:`int` or :class:`str`. Despite not being a type, ``None`` is also accepted here. Returns: ~.PrimitiveType: The instantiated PrimitiveType object. """ # Primitives have no import, and no module to reference, so the # address just uses the name of the class (e.g. "int", "str"). return cls(meta=metadata.Metadata(address=metadata.Address( name='None' if primitive_type is None else primitive_type.__name__, )), python_type=primitive_type)
def meta(self) -> metadata.Metadata: """Return a Metadata object.""" return metadata.Metadata( address=metadata.Address( module='operation', package=('google', 'api_core'), ), documentation=descriptor_pb2.SourceCodeInfo.Location( leading_comments='An object representing a long-running ' 'operation. \n\n' 'The result type for the operation will be ' ':class:`~.{module}.{name}`: {doc}'.format( doc=self.lro_response.meta.doc, module=self.lro_response.python_module, name=self.lro_response.name, ), ), )
def _get_methods(self, methods: Sequence[descriptor_pb2.MethodDescriptorProto], service_address: metadata.Address, path: Tuple[int, ...], ) -> Mapping[str, wrappers.Method]: """Return a dictionary of wrapped methods for the given service. Args: methods (Sequence[~.descriptor_pb2.MethodDescriptorProto]): A sequence of protobuf method objects. service_address (~.metadata.Address): An address object for the service, denoting the location of these methods. path (Tuple[int]): The source location path thus far, as understood by ``SourceCodeInfo.Location``. Returns: Mapping[str, ~.wrappers.Method]: A ordered mapping of :class:`~.wrappers.Method` objects. """ # Iterate over the methods and collect them into a dictionary. answer: Dict[str, wrappers.Method] = collections.OrderedDict() for i, meth_pb in enumerate(methods): retry, timeout = self._get_retry_and_timeout( service_address, meth_pb ) # Create the method wrapper object. answer[meth_pb.name] = wrappers.Method( input=self.api_messages[meth_pb.input_type.lstrip('.')], lro=self._maybe_get_lro(service_address, meth_pb), extended_lro=self._maybe_get_extended_lro( service_address, meth_pb, ), method_pb=meth_pb, meta=metadata.Metadata( address=service_address.child(meth_pb.name, path + (i,)), documentation=self.docs.get(path + (i,), self.EMPTY), ), output=self.api_messages[meth_pb.output_type.lstrip('.')], retry=retry, timeout=timeout, ) # Done; return the answer. return answer
def meta(self) -> metadata.Metadata: """Return a Metadata object.""" return metadata.Metadata( address=metadata.Address( name='Operation', module='operation', package=('google', 'api_core'), collisions=self.lro_response.meta.address.collisions, ), documentation=descriptor_pb2.SourceCodeInfo.Location( leading_comments='An object representing a long-running ' 'operation. \n\n' 'The result type for the operation will be ' ':class:`{ident}`: {doc}'.format( doc=self.lro_response.meta.doc, ident=self.lro_response.ident.sphinx, ), ), )
def _get_fields( self, field_pbs: List[descriptor_pb2.FieldDescriptorProto], address: metadata.Address, path: Tuple[int], ) -> Mapping[str, wrappers.Field]: """Return a dictionary of wrapped fields for the given message. Args: fields (Sequence[~.descriptor_pb2.FieldDescriptorProto]): A sequence of protobuf field objects. address (~.metadata.Address): An address object denoting the location of these fields. path (Tuple[int]): The source location path thus far, as understood by ``SourceCodeInfo.Location``. Returns: Mapping[str, ~.wrappers.Field]: A ordered mapping of :class:`~.wrappers.Field` objects. """ # Iterate over the fields and collect them into a dictionary. # # The saving of the enum and message types rely on protocol buffers' # naming rules to trust that they will never collide. # # Note: If this field is a recursive reference to its own message, # then the message will not be in `all_messages` yet (because the # message wrapper is not yet created, because it needs this object # first) and this will be None. This case is addressed in the # `_load_message` method. answer = collections.OrderedDict() for field_pb, i in zip(field_pbs, range(0, sys.maxsize)): answer[field_pb.name] = wrappers.Field( field_pb=field_pb, enum=self.all_enums.get(field_pb.type_name.lstrip('.')), message=self.all_messages.get(field_pb.type_name.lstrip('.')), meta=metadata.Metadata( address=address, documentation=self.docs.get(path + (i, ), self.EMPTY), ), ) # Done; return the answer. return answer
def test_mock_value_enum(): values = [ descriptor_pb2.EnumValueDescriptorProto(name='UNSPECIFIED', number=0), descriptor_pb2.EnumValueDescriptorProto(name='SPECIFIED', number=1), ] enum = wrappers.EnumType( values=[wrappers.EnumValueType(enum_value_pb=i) for i in values], enum_pb=descriptor_pb2.EnumDescriptorProto(value=values), meta=metadata.Metadata(address=metadata.Address( module='bogus', name='Enumerable', )), ) field = make_field( type='TYPE_ENUM', type_name='bogus.Enumerable', enum=enum, ) assert field.mock_value == 'bogus.Enumerable.SPECIFIED'