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

@@ -225,7 +225,9 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
cdef cppclass DynamicStruct:
cppclass Reader:
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 hasByField"has"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema()
Maybe[StructSchema.Field] which()
MessageSize totalSize()
@@ -239,10 +241,15 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicStruct_Builder()
DynamicStruct_Builder(DynamicStruct_Builder &)
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 hasByField"has"(StructSchema.Field) 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 *) 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()
Maybe[StructSchema.Field] which()
void adopt(char *, DynamicOrphan) except +reraise_kj_exception

View File

@@ -872,9 +872,15 @@ cdef class _DynamicStructReader:
def __getattr__(self, field):
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):
return self.thisptr.has(field)
def _has_by_field(self, _StructSchemaField field):
return self.thisptr.hasByField(field.thisptr)
cpdef _which(self):
"""Returns the enum corresponding to the union in this struct
@@ -1048,18 +1054,28 @@ cdef class _DynamicStructBuilder:
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):
return self._get(field)
cdef _set(self, field, value):
_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):
self._set(field, value)
def _has(self, 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):
"""Method for initializing fields that are of type union/struct/list
@@ -1080,6 +1096,26 @@ cdef class _DynamicStructBuilder:
else:
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):
"""Method for initializing fields that are of type list (of structs)
@@ -2129,12 +2165,13 @@ cdef class _Schema:
cdef class _StructSchema:
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):
self.thisptr = other
self.__fieldnames = None
self.__union_fields = None
self.__non_union_fields = None
self.__fields = None
return self
property fieldnames:
@@ -2170,6 +2207,17 @@ cdef class _StructSchema:
for i in xrange(nfields))
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:
"""The raw schema node"""
def __get__(self):
@@ -2315,13 +2363,14 @@ class _StructModule(object):
for field in schema.node.struct.fields:
if field.which() == 'group':
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:
continue
union_module = _StructModuleWhich()
setattr(union_module, 'schema', union_schema)
setattr(union_module, 'schema', raw_schema.as_struct())
for union_field in union_schema.fields:
setattr(union_module, union_field.name, union_field.discriminantValue)
setattr(self, name, union_module)

View File

@@ -192,6 +192,75 @@ def test_addressbook_resizable(addressbook):
f = open('example', 'r')
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
def all_types():

View File

@@ -187,3 +187,12 @@ def test_to_dict_enum(addressbook):
field = person.to_dict()['phones'][0]['type']
assert isstr(field)
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)