Add {_get,_set,_has,_init}_by_field methods for faster field access

This commit is contained in:
Jason Paryani
2014-09-03 15:20:26 -07:00
parent a0143260d6
commit ea1be42252
4 changed files with 180 additions and 46 deletions

View File

@@ -43,7 +43,7 @@ cdef extern from "kj/exception.h" namespace " ::kj":
int getDurability() int getDurability()
StringPtr getDescription() StringPtr getDescription()
cdef extern from "kj/memory.h" namespace " ::kj": cdef extern from "kj/memory.h" namespace " ::kj":
cdef cppclass Own[T]: cdef cppclass Own[T]:
T& operator*() T& operator*()
Own[TwoPartyVatNetwork] makeTwoPartyVatNetwork" ::kj::heap< ::capnp::TwoPartyVatNetwork>"(AsyncIoStream& stream, Side) Own[TwoPartyVatNetwork] makeTwoPartyVatNetwork" ::kj::heap< ::capnp::TwoPartyVatNetwork>"(AsyncIoStream& stream, Side)
@@ -225,7 +225,9 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicStruct: cdef cppclass DynamicStruct:
cppclass Reader: cppclass Reader:
DynamicValueForward.Reader get(char *) except +reraise_kj_exception DynamicValueForward.Reader get(char *) except +reraise_kj_exception
DynamicValueForward.Reader getByField"get"(StructSchema.Field) except +reraise_kj_exception
bint has(char *) except +reraise_kj_exception bint has(char *) except +reraise_kj_exception
bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema() StructSchema getSchema()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
MessageSize totalSize() MessageSize totalSize()
@@ -239,10 +241,15 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicStruct_Builder() DynamicStruct_Builder()
DynamicStruct_Builder(DynamicStruct_Builder &) DynamicStruct_Builder(DynamicStruct_Builder &)
DynamicValueForward.Builder get(char *) except +reraise_kj_exception DynamicValueForward.Builder get(char *) except +reraise_kj_exception
DynamicValueForward.Builder getByField"get"(StructSchema.Field) except +reraise_kj_exception
bint has(char *) except +reraise_kj_exception bint has(char *) except +reraise_kj_exception
bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception
void set(char *, DynamicValueForward.Reader) except +reraise_kj_exception void set(char *, DynamicValueForward.Reader) except +reraise_kj_exception
void setByField"set"(StructSchema.Field, DynamicValueForward.Reader) except +reraise_kj_exception
DynamicValueForward.Builder init(char *, uint size) except +reraise_kj_exception DynamicValueForward.Builder init(char *, uint size) except +reraise_kj_exception
DynamicValueForward.Builder init(char *) except +reraise_kj_exception DynamicValueForward.Builder init(char *) except +reraise_kj_exception
DynamicValueForward.Builder initByField"init"(StructSchema.Field, uint size) except +reraise_kj_exception
DynamicValueForward.Builder initByField"init"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema() StructSchema getSchema()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
void adopt(char *, DynamicOrphan) except +reraise_kj_exception void adopt(char *, DynamicOrphan) except +reraise_kj_exception

View File

@@ -101,7 +101,7 @@ cdef public VoidPromise * call_server_method(PyObject * _server, char * _method_
setattr(results, arg_name, arg_val) setattr(results, arg_name, arg_val)
return NULL return NULL
cdef public C_Capability.Client * call_py_restorer(PyObject * _restorer, C_DynamicObject.Reader & _reader) except * with gil: cdef public C_Capability.Client * call_py_restorer(PyObject * _restorer, C_DynamicObject.Reader & _reader) except * with gil:
restorer = <object>_restorer restorer = <object>_restorer
reader = _DynamicObjectReader()._init(_reader, None) reader = _DynamicObjectReader()._init(_reader, None)
@@ -150,13 +150,13 @@ def _make_enum(enum_name, *sequential, **named):
enums['reverse_mapping'] = reverse enums['reverse_mapping'] = reverse
return type(enum_name, (), enums) return type(enum_name, (), enums)
_Nature = _make_enum('_Nature', _Nature = _make_enum('_Nature',
PRECONDITION = 0, PRECONDITION = 0,
LOCAL_BUG = 1, LOCAL_BUG = 1,
OS_ERROR = 2, OS_ERROR = 2,
NETWORK_FAILURE = 3, NETWORK_FAILURE = 3,
OTHER = 4) OTHER = 4)
_Durability = _make_enum('_Durability', _Durability = _make_enum('_Durability',
PERMANENT = 0, PERMANENT = 0,
TEMPORARY = 1, TEMPORARY = 1,
OVERLOADED = 2) OVERLOADED = 2)
@@ -208,7 +208,7 @@ class KjException(Exception):
self.message = message self.message = message
self.nature = nature self.nature = nature
self.durability = durability self.durability = durability
@property @property
def file(self): def file(self):
return self.wrapper.file return self.wrapper.file
@@ -247,7 +247,7 @@ cdef public object wrap_kj_exception(capnp.Exception & exception) with gil:
cdef public object wrap_kj_exception_for_reraise(capnp.Exception & exception) with gil: cdef public object wrap_kj_exception_for_reraise(capnp.Exception & exception) with gil:
wrapper = _KjExceptionWrapper()._init(exception) wrapper = _KjExceptionWrapper()._init(exception)
wrapper_msg = str(wrapper) wrapper_msg = str(wrapper)
nature = wrapper.nature nature = wrapper.nature
if wrapper.nature == 'PRECONDITION': if wrapper.nature == 'PRECONDITION':
@@ -415,7 +415,7 @@ cdef class _DynamicResizableListBuilder:
person = addressbook.Person.new_message() person = addressbook.Person.new_message()
phones = person.init_resizable_list('phones') # This returns a _DynamicResizableListBuilder phones = person.init_resizable_list('phones') # This returns a _DynamicResizableListBuilder
phone = phones.add() phone = phones.add()
phone.number = 'foo' phone.number = 'foo'
phone = phones.add() phone = phones.add()
@@ -447,7 +447,7 @@ cdef class _DynamicResizableListBuilder:
orphan_val = orphan.get() orphan_val = orphan.get()
self._list.append((orphan, orphan_val)) self._list.append((orphan, orphan_val))
return orphan_val return orphan_val
def __getitem__(self, index): def __getitem__(self, index):
return self._list[index][1] return self._list[index][1]
@@ -477,7 +477,7 @@ cdef class _DynamicListBuilder:
person = addressbook.Person.new_message() person = addressbook.Person.new_message()
phones = person.init('phones', 2) # This returns a _DynamicListBuilder phones = person.init('phones', 2) # This returns a _DynamicListBuilder
phone = phones[0] phone = phones[0]
phone.number = 'foo' phone.number = 'foo'
phone = phones[1] phone = phones[1]
@@ -872,9 +872,15 @@ cdef class _DynamicStructReader:
def __getattr__(self, field): def __getattr__(self, field):
return to_python_reader(self.thisptr.get(field), self._parent) return to_python_reader(self.thisptr.get(field), self._parent)
def _get_by_field(self, _StructSchemaField field):
return to_python_reader(self.thisptr.getByField(field.thisptr), self._parent)
def _has(self, field): def _has(self, field):
return self.thisptr.has(field) return self.thisptr.has(field)
def _has_by_field(self, _StructSchemaField field):
return self.thisptr.hasByField(field.thisptr)
cpdef _which(self): cpdef _which(self):
"""Returns the enum corresponding to the union in this struct """Returns the enum corresponding to the union in this struct
@@ -945,7 +951,7 @@ cdef class _DynamicStructBuilder:
This class is almost a 1 for 1 wrapping of the Cap'n Proto C++ DynamicStruct::Builder. The only difference is that instead of a `get`/`set` method, __getattr__/__setattr__ is overloaded and the field name is passed onto the C++ equivalent function. This means you just use . syntax to access or set any field. For field names that don't follow valid python naming convention for fields, use the global functions :py:func:`getattr`/:py:func:`setattr`:: This class is almost a 1 for 1 wrapping of the Cap'n Proto C++ DynamicStruct::Builder. The only difference is that instead of a `get`/`set` method, __getattr__/__setattr__ is overloaded and the field name is passed onto the C++ equivalent function. This means you just use . syntax to access or set any field. For field names that don't follow valid python naming convention for fields, use the global functions :py:func:`getattr`/:py:func:`setattr`::
person = addressbook.Person.new_message() # This returns a _DynamicStructBuilder person = addressbook.Person.new_message() # This returns a _DynamicStructBuilder
person.name = 'foo' # using . syntax person.name = 'foo' # using . syntax
print person.name # using . syntax print person.name # using . syntax
@@ -971,15 +977,15 @@ cdef class _DynamicStructBuilder:
def write(self, file): def write(self, file):
"""Writes the struct's containing message to the given file object in unpacked binary format. """Writes the struct's containing message to the given file object in unpacked binary format.
This is a shortcut for calling capnp._write_message_to_fd(). This can only be called on the This is a shortcut for calling capnp._write_message_to_fd(). This can only be called on the
message's root struct. message's root struct.
:type file: file :type file: file
:param file: A file or socket object (or anything with a fileno() method), open for write. :param file: A file or socket object (or anything with a fileno() method), open for write.
:rtype: void :rtype: void
:Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct. :Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct.
""" """
self._check_write() self._check_write()
@@ -988,15 +994,15 @@ cdef class _DynamicStructBuilder:
def write_packed(self, file): def write_packed(self, file):
"""Writes the struct's containing message to the given file object in packed binary format. """Writes the struct's containing message to the given file object in packed binary format.
This is a shortcut for calling capnp._write_packed_message_to_fd(). This can only be called on This is a shortcut for calling capnp._write_packed_message_to_fd(). This can only be called on
the message's root struct. the message's root struct.
:type file: file :type file: file
:param file: A file or socket object (or anything with a fileno() method), open for write. :param file: A file or socket object (or anything with a fileno() method), open for write.
:rtype: void :rtype: void
:Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct. :Raises: :exc:`exceptions.ValueError` if this isn't the message's root struct.
""" """
self._check_write() self._check_write()
@@ -1047,23 +1053,33 @@ cdef class _DynamicStructBuilder:
cdef C_DynamicValue.Builder value = self.thisptr.get(field) cdef C_DynamicValue.Builder value = self.thisptr.get(field)
return to_python_builder(value, self._parent) return to_python_builder(value, self._parent)
def _get_by_field(self, _StructSchemaField field):
return to_python_builder(self.thisptr.getByField(field.thisptr), self._parent)
def __getattr__(self, field): def __getattr__(self, field):
return self._get(field) return self._get(field)
cdef _set(self, field, value): cdef _set(self, field, value):
_setDynamicField(self.thisptr, field, value, self._parent) _setDynamicField(self.thisptr, field, value, self._parent)
def _set_by_field(self, _StructSchemaField field, value):
# TODO: make this faster
_setDynamicField(self.thisptr, field.proto.name, value, self._parent)
def __setattr__(self, field, value): def __setattr__(self, field, value):
self._set(field, value) self._set(field, value)
def _has(self, field): def _has(self, field):
return self.thisptr.has(field) return self.thisptr.has(field)
def _has_by_field(self, _StructSchemaField field):
return self.thisptr.hasByField(field.thisptr)
cpdef init(self, field, size=None): cpdef init(self, field, size=None):
"""Method for initializing fields that are of type union/struct/list """Method for initializing fields that are of type union/struct/list
Typically, you don't have to worry about initializing structs/unions, so this method is mainly for lists. Typically, you don't have to worry about initializing structs/unions, so this method is mainly for lists.
:type field: str :type field: str
:param field: The field name to initialize :param field: The field name to initialize
@@ -1080,10 +1096,30 @@ cdef class _DynamicStructBuilder:
else: else:
return to_python_builder(self.thisptr.init(field, size), self._parent) return to_python_builder(self.thisptr.init(field, size), self._parent)
def _init_by_field(self, _StructSchemaField field, size=None):
"""Method for initializing fields that are of type union/struct/list
Typically, you don't have to worry about initializing structs/unions, so this method is mainly for lists.
:type field: str
:param field: The field name to initialize
:type size: int
:param size: The size of the list to initiialize. This should be None for struct/union initialization.
:rtype: :class:`_DynamicStructBuilder` or :class:`_DynamicListBuilder`
:Raises: :exc:`exceptions.ValueError` if the field isn't in this struct
"""
if size is None:
return to_python_builder(self.thisptr.initByField(field.thisptr), self._parent)
else:
return to_python_builder(self.thisptr.initByField(field.thisptr, size), self._parent)
cpdef init_resizable_list(self, field): cpdef init_resizable_list(self, field):
"""Method for initializing fields that are of type list (of structs) """Method for initializing fields that are of type list (of structs)
This version of init returns a :class:`_DynamicResizableListBuilder` that allows you to add members one at a time (ie. if you don't know the size for sure). This is only meant for lists of Cap'n Proto objects, since for primitive types you can just define a normal python list and fill it yourself. This version of init returns a :class:`_DynamicResizableListBuilder` that allows you to add members one at a time (ie. if you don't know the size for sure). This is only meant for lists of Cap'n Proto objects, since for primitive types you can just define a normal python list and fill it yourself.
.. warning:: You need to call :meth:`_DynamicResizableListBuilder.finish` on the list object before serializing the Cap'n Proto message. Failure to do so will cause your objects not to be written out as well as leaking orphan structs into your message. .. warning:: You need to call :meth:`_DynamicResizableListBuilder.finish` on the list object before serializing the Cap'n Proto message. Failure to do so will cause your objects not to be written out as well as leaking orphan structs into your message.
@@ -1583,7 +1619,7 @@ cdef class _VoidPromise:
cpdef cancel(self, numParents=1) except +reraise_kj_exception: cpdef cancel(self, numParents=1) except +reraise_kj_exception:
if numParents > 0 and hasattr(self._parent, 'cancel'): if numParents > 0 and hasattr(self._parent, 'cancel'):
self._parent.cancel(numParents - 1) self._parent.cancel(numParents - 1)
self.is_consumed = True self.is_consumed = True
del self.thisptr del self.thisptr
self.thisptr = NULL self.thisptr = NULL
@@ -1670,7 +1706,7 @@ cdef class _RemotePromise:
cpdef cancel(self, numParents=1) except +reraise_kj_exception: cpdef cancel(self, numParents=1) except +reraise_kj_exception:
if numParents > 0 and hasattr(self._parent, 'cancel'): if numParents > 0 and hasattr(self._parent, 'cancel'):
self._parent.cancel(numParents - 1) self._parent.cancel(numParents - 1)
self.is_consumed = True self.is_consumed = True
del self.thisptr del self.thisptr
self.thisptr = NULL self.thisptr = NULL
@@ -1962,7 +1998,7 @@ cdef class TwoPartyClient:
return sock return sock
cpdef restore(self, objectId) except +reraise_kj_exception: cpdef restore(self, objectId) except +reraise_kj_exception:
cdef _MessageBuilder builder cdef _MessageBuilder builder
cdef _MessageReader reader cdef _MessageReader reader
cdef _DynamicObjectBuilder object_builder cdef _DynamicObjectBuilder object_builder
cdef _DynamicObjectReader object_reader cdef _DynamicObjectReader object_reader
@@ -2129,12 +2165,13 @@ cdef class _Schema:
cdef class _StructSchema: cdef class _StructSchema:
cdef C_StructSchema thisptr cdef C_StructSchema thisptr
cdef object __fieldnames, __union_fields, __non_union_fields cdef object __fieldnames, __union_fields, __non_union_fields, __fields
cdef _init(self, C_StructSchema other): cdef _init(self, C_StructSchema other):
self.thisptr = other self.thisptr = other
self.__fieldnames = None self.__fieldnames = None
self.__union_fields = None self.__union_fields = None
self.__non_union_fields = None self.__non_union_fields = None
self.__fields = None
return self return self
property fieldnames: property fieldnames:
@@ -2170,6 +2207,17 @@ cdef class _StructSchema:
for i in xrange(nfields)) for i in xrange(nfields))
return self.__non_union_fields return self.__non_union_fields
property fields:
"""A tuple of the field names in the struct."""
def __get__(self):
if self.__fields is not None:
return self.__fields
fieldlist = self.thisptr.getFields()
nfields = fieldlist.size()
self.__fields = {<char*>fieldlist[i].getProto().getName().cStr() : _StructSchemaField()._init(fieldlist[i], self)
for i in xrange(nfields)}
return self.__fields
property node: property node:
"""The raw schema node""" """The raw schema node"""
def __get__(self): def __get__(self):
@@ -2315,13 +2363,14 @@ class _StructModule(object):
for field in schema.node.struct.fields: for field in schema.node.struct.fields:
if field.which() == 'group': if field.which() == 'group':
name = field.name.capitalize() name = field.name.capitalize()
union_schema = schema.get_dependency(field.group.typeId).node.struct raw_schema = schema.get_dependency(field.group.typeId)
union_schema = raw_schema.node.struct
if union_schema.discriminantCount == 0: if union_schema.discriminantCount == 0:
continue continue
union_module = _StructModuleWhich() union_module = _StructModuleWhich()
setattr(union_module, 'schema', union_schema) setattr(union_module, 'schema', raw_schema.as_struct())
for union_field in union_schema.fields: for union_field in union_schema.fields:
setattr(union_module, union_field.name, union_field.discriminantValue) setattr(union_module, union_field.name, union_field.discriminantValue)
setattr(self, name, union_module) setattr(self, name, union_module)
@@ -2331,7 +2380,7 @@ class _StructModule(object):
:type file: file :type file: file
:param file: A python file-like object. It must be a "real" file, with a `fileno()` method. :param file: A python file-like object. It must be a "real" file, with a `fileno()` method.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2346,7 +2395,7 @@ class _StructModule(object):
:type file: file :type file: file
:param file: A python file-like object. It must be a "real" file, with a `fileno()` method. :param file: A python file-like object. It must be a "real" file, with a `fileno()` method.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2361,7 +2410,7 @@ class _StructModule(object):
:type file: file :type file: file
:param file: A python file-like object. It must be a "real" file, with a `fileno()` method. :param file: A python file-like object. It must be a "real" file, with a `fileno()` method.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2376,7 +2425,7 @@ class _StructModule(object):
:type file: file :type file: file
:param file: A python file-like object. It must be a "real" file, with a `fileno()` method. :param file: A python file-like object. It must be a "real" file, with a `fileno()` method.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2391,7 +2440,7 @@ class _StructModule(object):
:type buf: buffer :type buf: buffer
:param buf: Any Python object that supports the buffer interface. :param buf: Any Python object that supports the buffer interface.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2415,7 +2464,7 @@ class _StructModule(object):
:type buf: buffer :type buf: buffer
:param buf: Any Python object that supports the readable buffer interface. :param buf: Any Python object that supports the readable buffer interface.
:type traversal_limit_in_words: int :type traversal_limit_in_words: int
:param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024. :param traversal_limit_in_words: Limits how many total words of data are allowed to be traversed. Is actually a uint64_t, and values can be up to 2^64-1. Default is 8*1024*1024.
@@ -2497,7 +2546,7 @@ cdef class SchemaParser:
return ret return ret
def load(self, file_name, display_name=None, imports=[]): def load(self, file_name, display_name=None, imports=[]):
"""Load a Cap'n Proto schema from a file """Load a Cap'n Proto schema from a file
You will have to load a schema before you can begin doing anything You will have to load a schema before you can begin doing anything
meaningful with this library. Loading a schema is much like loading meaningful with this library. Loading a schema is much like loading
@@ -2663,7 +2712,7 @@ cdef class _MessageBuilder:
:return: An AnyPointer that you can set fields in :return: An AnyPointer that you can set fields in
""" """
return _DynamicObjectBuilder()._init(self.thisptr.getRootAnyPointer(), self) return _DynamicObjectBuilder()._init(self.thisptr.getRootAnyPointer(), self)
cpdef set_root(self, value) except +reraise_kj_exception: cpdef set_root(self, value) except +reraise_kj_exception:
"""A method for instantiating Cap'n Proto structs by copying from an existing struct """A method for instantiating Cap'n Proto structs by copying from an existing struct
@@ -2819,7 +2868,7 @@ cdef class _PackedMessageReader(_MessageReader):
opts.traversalLimitInWords = traversal_limit_in_words opts.traversalLimitInWords = traversal_limit_in_words
if nesting_limit is not None: if nesting_limit is not None:
opts.nestingLimit = nesting_limit opts.nestingLimit = nesting_limit
self.thisptr = new schema_cpp.PackedMessageReader(stream, opts) self.thisptr = new schema_cpp.PackedMessageReader(stream, opts)
return self return self
@@ -2836,13 +2885,13 @@ cdef class _PackedMessageReaderBytes(_MessageReader):
opts.traversalLimitInWords = traversal_limit_in_words opts.traversalLimitInWords = traversal_limit_in_words
if nesting_limit is not None: if nesting_limit is not None:
opts.nestingLimit = nesting_limit opts.nestingLimit = nesting_limit
cdef const void *ptr cdef const void *ptr
cdef Py_ssize_t sz cdef Py_ssize_t sz
PyObject_AsReadBuffer(buf, &ptr, &sz) PyObject_AsReadBuffer(buf, &ptr, &sz)
self.stream = new schema_cpp.ArrayInputStream(schema_cpp.ByteArrayPtr(<byte *>ptr, sz)) self.stream = new schema_cpp.ArrayInputStream(schema_cpp.ByteArrayPtr(<byte *>ptr, sz))
self.thisptr = new schema_cpp.PackedMessageReader(deref(self.stream), opts) self.thisptr = new schema_cpp.PackedMessageReader(deref(self.stream), opts)
def __dealloc__(self): def __dealloc__(self):
@@ -2873,7 +2922,7 @@ cdef class _InputMessageReader(_MessageReader):
opts.traversalLimitInWords = traversal_limit_in_words opts.traversalLimitInWords = traversal_limit_in_words
if nesting_limit is not None: if nesting_limit is not None:
opts.nestingLimit = nesting_limit opts.nestingLimit = nesting_limit
self.thisptr = new schema_cpp.InputStreamMessageReader(stream, opts) self.thisptr = new schema_cpp.InputStreamMessageReader(stream, opts)
return self return self
@@ -2896,7 +2945,7 @@ cdef class _PackedFdMessageReader(_MessageReader):
opts.traversalLimitInWords = traversal_limit_in_words opts.traversalLimitInWords = traversal_limit_in_words
if nesting_limit is not None: if nesting_limit is not None:
opts.nestingLimit = nesting_limit opts.nestingLimit = nesting_limit
self.thisptr = new schema_cpp.PackedFdMessageReader(fd, opts) self.thisptr = new schema_cpp.PackedFdMessageReader(fd, opts)
cdef class _MultipleMessageReader: cdef class _MultipleMessageReader:
@@ -2909,7 +2958,7 @@ cdef class _MultipleMessageReader:
self.schema = schema self.schema = schema
self.traversal_limit_in_words = traversal_limit_in_words self.traversal_limit_in_words = traversal_limit_in_words
self.nesting_limit = nesting_limit self.nesting_limit = nesting_limit
self.stream = new schema_cpp.FdInputStream(fd) self.stream = new schema_cpp.FdInputStream(fd)
self.buffered_stream = new schema_cpp.BufferedInputStreamWrapper(deref(self.stream)) self.buffered_stream = new schema_cpp.BufferedInputStreamWrapper(deref(self.stream))
@@ -2940,7 +2989,7 @@ cdef class _MultiplePackedMessageReader:
self.schema = schema self.schema = schema
self.traversal_limit_in_words = traversal_limit_in_words self.traversal_limit_in_words = traversal_limit_in_words
self.nesting_limit = nesting_limit self.nesting_limit = nesting_limit
self.stream = new schema_cpp.FdInputStream(fd) self.stream = new schema_cpp.FdInputStream(fd)
self.buffered_stream = new schema_cpp.BufferedInputStreamWrapper(deref(self.stream)) self.buffered_stream = new schema_cpp.BufferedInputStreamWrapper(deref(self.stream))
@@ -2971,7 +3020,7 @@ cdef class _FlatArrayMessageReader(_MessageReader):
opts.traversalLimitInWords = traversal_limit_in_words opts.traversalLimitInWords = traversal_limit_in_words
if nesting_limit is not None: if nesting_limit is not None:
opts.nestingLimit = nesting_limit opts.nestingLimit = nesting_limit
cdef const void *ptr cdef const void *ptr
cdef Py_ssize_t sz cdef Py_ssize_t sz
PyObject_AsReadBuffer(buf, &ptr, &sz) PyObject_AsReadBuffer(buf, &ptr, &sz)
@@ -3062,7 +3111,7 @@ def _write_packed_message_to_fd(int fd, _MessageBuilder message):
_global_schema_parser = None _global_schema_parser = None
def load(file_name, display_name=None, imports=[]): def load(file_name, display_name=None, imports=[]):
"""Load a Cap'n Proto schema from a file """Load a Cap'n Proto schema from a file
You will have to load a schema before you can begin doing anything You will have to load a schema before you can begin doing anything
meaningful with this library. Loading a schema is much like loading meaningful with this library. Loading a schema is much like loading

View File

@@ -156,7 +156,7 @@ def test_addressbook_resizable(addressbook):
bob.employment.unemployed = None bob.employment.unemployed = None
people.finish() people.finish()
addresses.write(file) addresses.write(file)
@@ -192,6 +192,75 @@ def test_addressbook_resizable(addressbook):
f = open('example', 'r') f = open('example', 'r')
printAddressBook(f) printAddressBook(f)
def test_addressbook_explicit_fields(addressbook):
def writeAddressBook(file):
addresses = addressbook.AddressBook.new_message()
address_fields = addressbook.AddressBook.schema.fields
person_fields = addressbook.Person.schema.fields
phone_fields = addressbook.Person.PhoneNumber.schema.fields
people = addresses._init_by_field(address_fields['people'], 2)
alice = people[0]
alice._set_by_field(person_fields['id'], 123)
alice._set_by_field(person_fields['name'], 'Alice')
alice._set_by_field(person_fields['email'], 'alice@example.com')
alicePhones = alice._init_by_field(person_fields['phones'], 1)
alicePhones[0]._set_by_field(phone_fields['number'], "555-1212")
alicePhones[0]._set_by_field(phone_fields['type'], 'mobile')
employment = alice._get_by_field(person_fields['employment'])
employment._set_by_field(addressbook.Person.Employment.schema.fields['school'], "MIT")
bob = people[1]
bob._set_by_field(person_fields['id'], 456)
bob._set_by_field(person_fields['name'], 'Bob')
bob._set_by_field(person_fields['email'], 'bob@example.com')
bobPhones = bob._init_by_field(person_fields['phones'], 2)
bobPhones[0]._set_by_field(phone_fields['number'], "555-4567")
bobPhones[0]._set_by_field(phone_fields['type'], 'home')
bobPhones[1]._set_by_field(phone_fields['number'], "555-7654")
bobPhones[1]._set_by_field(phone_fields['type'], 'work')
employment = bob._get_by_field(person_fields['employment'])
employment._set_by_field(addressbook.Person.Employment.schema.fields['unemployed'], None)
addresses.write(file)
def printAddressBook(file):
addresses = addressbook.AddressBook.read(file)
address_fields = addressbook.AddressBook.schema.fields
person_fields = addressbook.Person.schema.fields
phone_fields = addressbook.Person.PhoneNumber.schema.fields
people = addresses._get_by_field(address_fields['people'])
alice = people[0]
assert alice._get_by_field(person_fields['id']) == 123
assert alice._get_by_field(person_fields['name']) == 'Alice'
assert alice._get_by_field(person_fields['email']) == 'alice@example.com'
alicePhones = alice._get_by_field(person_fields['phones'])
assert alicePhones[0]._get_by_field(phone_fields['number']) == "555-1212"
assert alicePhones[0]._get_by_field(phone_fields['type']) == 'mobile'
employment = alice._get_by_field(person_fields['employment'])
employment._get_by_field(addressbook.Person.Employment.schema.fields['school']) == "MIT"
bob = people[1]
assert bob._get_by_field(person_fields['id']) == 456
assert bob._get_by_field(person_fields['name']) == 'Bob'
assert bob._get_by_field(person_fields['email']) == 'bob@example.com'
bobPhones = bob._get_by_field(person_fields['phones'])
assert bobPhones[0]._get_by_field(phone_fields['number']) == "555-4567"
assert bobPhones[0]._get_by_field(phone_fields['type']) == 'home'
assert bobPhones[1]._get_by_field(phone_fields['number']) == "555-7654"
assert bobPhones[1]._get_by_field(phone_fields['type']) == 'work'
employment = bob._get_by_field(person_fields['employment'])
employment._get_by_field(addressbook.Person.Employment.schema.fields['unemployed']) == None
f = open('example', 'w')
writeAddressBook(f)
f = open('example', 'r')
printAddressBook(f)
@pytest.fixture @pytest.fixture
def all_types(): def all_types():

View File

@@ -187,3 +187,12 @@ def test_to_dict_enum(addressbook):
field = person.to_dict()['phones'][0]['type'] field = person.to_dict()['phones'][0]['type']
assert isstr(field) assert isstr(field)
assert field == 'mobile' assert field == 'mobile'
def test_explicit_field(addressbook):
person = addressbook.Person.new_message(**{'name': 'Test'})
name_field = addressbook.Person.schema.fields['name']
assert person.name == person._get_by_field(name_field)
assert person.name == person.as_reader()._get_by_field(name_field)