Initial version of code generating plugin

The plugin generates higher performance compiled versions of the structs
This commit is contained in:
Jason Paryani
2014-09-04 14:21:27 -07:00
parent f12f826a85
commit 93352a31d2
10 changed files with 597 additions and 9 deletions

37
capnp/_gen.py Normal file
View File

@@ -0,0 +1,37 @@
from __future__ import print_function
import capnp
import schema_capnp
import sys
from jinja2 import Environment, PackageLoader
def main():
env = Environment(loader=PackageLoader('capnp', 'templates'))
env.filters['format_name'] = lambda name: name[name.find(':')+1:]
code = schema_capnp.CodeGeneratorRequest.read(sys.stdin)
code=code.to_dict()
code['nodes'] = [node for node in code['nodes'] if 'struct' in node]
for node in code['nodes']:
displayName = node['displayName']
parent, path = displayName.split(':')
node['module_path'] = parent.replace('.', '_') + '.' + '.'.join([x[0].upper() + x[1:] for x in path.split('.')])
node['module_name'] = path.replace('.', '_')
node['schema'] = '_{}_Schema'.format(node['module_name'])
is_union = False
for field in node['struct']['fields']:
if field['discriminantValue'] != 65535:
is_union = True
node['is_union'] = is_union
module = env.get_template('module.pyx')
filename = code['requestedFiles'][0]['filename'].replace('.', '_') + '_cython.pyx'
# TODO: handle multiple files
with open(filename, 'w') as out:
out.write(module.render(code=code))
setup = env.get_template('setup.py')
with open('setup_capnp.py', 'w') as out:
out.write(setup.render(code=code))
print('You now need to build the cython module by running `python setup_capnp.py build_ext --inplace`.')
print()

View File

@@ -229,6 +229,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
bint has(char *) except +reraise_kj_exception bint has(char *) except +reraise_kj_exception
bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception bint hasByField"has"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema() StructSchema getSchema()
uint64_t getId"getSchema().getProto().getId"()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
MessageSize totalSize() MessageSize totalSize()
cppclass Pipeline: cppclass Pipeline:
@@ -251,6 +252,7 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
DynamicValueForward.Builder initByField"init"(StructSchema.Field, uint size) 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 DynamicValueForward.Builder initByField"init"(StructSchema.Field) except +reraise_kj_exception
StructSchema getSchema() StructSchema getSchema()
uint64_t getId"getSchema().getProto().getId"()
Maybe[StructSchema.Field] which() Maybe[StructSchema.Field] which()
void adopt(char *, DynamicOrphan) except +reraise_kj_exception void adopt(char *, DynamicOrphan) except +reraise_kj_exception
DynamicOrphan disown(char *) DynamicOrphan disown(char *)

View File

@@ -30,7 +30,7 @@ cdef class _DynamicStructReader:
cdef object _obj_to_pin cdef object _obj_to_pin
cdef object _schema cdef object _schema
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?) cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?, bint tryRegistry=?)
cpdef _get(self, field) cpdef _get(self, field)
cpdef _has(self, field) cpdef _has(self, field)
@@ -48,7 +48,7 @@ cdef class _DynamicStructBuilder:
cdef bint _is_written cdef bint _is_written
cdef object _schema cdef object _schema
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?) cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?, bint tryRegistry=?)
cdef _check_write(self) cdef _check_write(self)
cpdef to_bytes(_DynamicStructBuilder self) cpdef to_bytes(_DynamicStructBuilder self)
@@ -82,3 +82,8 @@ cdef class _Schema:
cpdef as_enum(self) cpdef as_enum(self)
cpdef get_dependency(self, id) cpdef get_dependency(self, id)
cpdef get_proto(self) cpdef get_proto(self)
cdef to_python_reader(C_DynamicValue.Reader self, object parent)
cdef to_python_builder(C_DynamicValue.Builder self, object parent)
cdef _to_dict(msg, bint verbose)
cdef _setDynamicFieldWithField(DynamicStruct_Builder thisptr, _StructSchemaField field, value, parent)

View File

@@ -33,6 +33,14 @@ _CAPNP_VERSION_MINOR = capnp.CAPNP_VERSION_MINOR
_CAPNP_VERSION_MICRO = capnp.CAPNP_VERSION_MICRO _CAPNP_VERSION_MICRO = capnp.CAPNP_VERSION_MICRO
_CAPNP_VERSION = capnp.CAPNP_VERSION _CAPNP_VERSION = capnp.CAPNP_VERSION
cdef dict _type_registry = {}
def register_type(id, klass):
_type_registry[id] = klass
def deregister_all_types():
_type_registry = {}
# By making it public, we'll be able to call it from capabilityHelper.h # By making it public, we'll be able to call it from capabilityHelper.h
cdef public object wrap_dynamic_struct_reader(Response & r) with gil: cdef public object wrap_dynamic_struct_reader(Response & r) with gil:
return _Response()._init_childptr(new Response(moveResponse(r)), None) return _Response()._init_childptr(new Response(moveResponse(r)), None)
@@ -671,6 +679,17 @@ cdef _setBaseString(_DynamicSetterClasses thisptr, field, value):
cdef C_DynamicValue.Reader temp = C_DynamicValue.Reader(temp_string) cdef C_DynamicValue.Reader temp = C_DynamicValue.Reader(temp_string)
thisptr.set(field, temp) thisptr.set(field, temp)
cdef _setBytesField(DynamicStruct_Builder thisptr, _StructSchemaField field, value):
cdef capnp.StringPtr temp_string = capnp.StringPtr(<char*>value, len(value))
cdef C_DynamicValue.Reader temp = C_DynamicValue.Reader(temp_string)
thisptr.setByField(field.thisptr, temp)
cdef _setBaseStringField(DynamicStruct_Builder thisptr, _StructSchemaField field, value):
encoded_value = value.encode()
cdef capnp.StringPtr temp_string = capnp.StringPtr(<char*>encoded_value, len(encoded_value))
cdef C_DynamicValue.Reader temp = C_DynamicValue.Reader(temp_string)
thisptr.setByField(field.thisptr, temp)
cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent): cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent):
cdef C_DynamicValue.Reader temp cdef C_DynamicValue.Reader temp
value_type = type(value) value_type = type(value)
@@ -717,6 +736,48 @@ cdef _setDynamicField(_DynamicSetterClasses thisptr, field, value, parent):
else: else:
raise ValueError("Tried to set field: '{}' with a value of: '{}' which is an unsupported type: '{}'".format(field, str(value), str(type(value)))) raise ValueError("Tried to set field: '{}' with a value of: '{}' which is an unsupported type: '{}'".format(field, str(value), str(type(value))))
cdef _setDynamicFieldWithField(DynamicStruct_Builder thisptr, _StructSchemaField field, value, parent):
cdef C_DynamicValue.Reader temp
value_type = type(value)
if value_type is int or value_type is long:
if value < 0:
temp = C_DynamicValue.Reader(<long long>value)
else:
temp = C_DynamicValue.Reader(<unsigned long long>value)
thisptr.setByField(field.thisptr, temp)
elif value_type is float:
temp = C_DynamicValue.Reader(<double>value)
thisptr.setByField(field.thisptr, temp)
elif value_type is bool:
temp = C_DynamicValue.Reader(<cbool>value)
thisptr.setByField(field.thisptr, temp)
elif value_type is bytes:
_setBytesField(thisptr, field, value)
elif isinstance(value, basestring):
_setBaseStringField(thisptr, field, value)
elif value_type is list:
builder = to_python_builder(thisptr.init(field.proto.name, len(value)), parent)
_from_list(builder, value)
elif value_type is dict:
builder = to_python_builder(thisptr.getByField(field.thisptr), parent)
_from_dict(builder, value)
elif value is None:
temp = C_DynamicValue.Reader(VOID)
thisptr.setByField(field.thisptr, temp)
elif value_type is _DynamicStructBuilder:
thisptr.setByField(field.thisptr, _extract_dynamic_struct_builder(value))
elif value_type is _DynamicStructReader:
thisptr.setByField(field.thisptr, _extract_dynamic_struct_reader(value))
elif value_type is _DynamicCapabilityClient:
thisptr.setByField(field.thisptr, _extract_dynamic_client(value))
elif value_type is _DynamicCapabilityServer or isinstance(value, _DynamicCapabilityServer):
thisptr.setByField(field.thisptr, _extract_dynamic_server(value))
elif value_type is _DynamicEnum:
thisptr.setByField(field.thisptr, _extract_dynamic_enum(value))
else:
raise ValueError("Tried to set field: '{}' with a value of: '{}' which is an unsupported type: '{}'".format(field, str(value), str(type(value))))
cdef _to_dict(msg, bint verbose): cdef _to_dict(msg, bint verbose):
msg_type = type(msg) msg_type = type(msg)
if msg_type is _DynamicListBuilder or msg_type is _DynamicListReader or msg_type is _DynamicResizableListBuilder: if msg_type is _DynamicListBuilder or msg_type is _DynamicListReader or msg_type is _DynamicResizableListBuilder:
@@ -736,6 +797,9 @@ cdef _to_dict(msg, bint verbose):
return ret return ret
if isinstance(msg, (_DynamicStructBuilder, _DynamicStructReader)):
return msg.to_dict()
if msg_type is _DynamicEnum: if msg_type is _DynamicEnum:
return str(msg) return str(msg)
@@ -864,11 +928,16 @@ cdef class _DynamicStructReader:
print person.name # using . syntax print person.name # using . syntax
print getattr(person, 'field-with-hyphens') # for names that are invalid for python, use getattr print getattr(person, 'field-with-hyphens') # for names that are invalid for python, use getattr
""" """
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=False): cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=False, bint tryRegistry = True):
self.thisptr = other self.thisptr = other
self._parent = parent self._parent = parent
self.is_root = isRoot self.is_root = isRoot
self._schema = None self._schema = None
if tryRegistry and len(_type_registry) > 0:
registered_type = _type_registry.get(self.thisptr.getId(), None)
if registered_type:
return registered_type[0](self)
return self return self
cpdef _get(self, field): cpdef _get(self, field):
@@ -966,12 +1035,17 @@ cdef class _DynamicStructBuilder:
setattr(person, 'field-with-hyphens', 'foo') # for names that are invalid for python, use setattr setattr(person, 'field-with-hyphens', 'foo') # for names that are invalid for python, use setattr
print getattr(person, 'field-with-hyphens') # for names that are invalid for python, use getattr print getattr(person, 'field-with-hyphens') # for names that are invalid for python, use getattr
""" """
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot = False): cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot = False, bint tryRegistry = True):
self.thisptr = other self.thisptr = other
self._parent = parent self._parent = parent
self.is_root = isRoot self.is_root = isRoot
self._is_written = False self._is_written = False
self._schema = None self._schema = None
if tryRegistry and len(_type_registry) > 0:
registered_type = _type_registry.get(self.thisptr.getId(), None)
if registered_type:
return registered_type[1](self)
return self return self
cdef _check_write(self): cdef _check_write(self):
@@ -1067,8 +1141,7 @@ cdef class _DynamicStructBuilder:
_setDynamicField(self.thisptr, field, value, self._parent) _setDynamicField(self.thisptr, field, value, self._parent)
cpdef _set_by_field(self, _StructSchemaField field, value): cpdef _set_by_field(self, _StructSchemaField field, value):
# TODO: make this faster _setDynamicFieldWithField(self.thisptr, field, value, self._parent)
_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)

383
capnp/schema.capnp Normal file
View File

@@ -0,0 +1,383 @@
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using Cxx = import "c++.capnp";
@0xa93fc509624c72d9;
$Cxx.namespace("capnp::schema");
using Id = UInt64;
# The globally-unique ID of a file, type, or annotation.
struct Node {
id @0 :Id;
displayName @1 :Text;
# Name to present to humans to identify this Node. You should not attempt to parse this. Its
# format could change. It is not guaranteed to be unique.
#
# (On Zooko's triangle, this is the node's nickname.)
displayNamePrefixLength @2 :UInt32;
# If you want a shorter version of `displayName` (just naming this node, without its surrounding
# scope), chop off this many characters from the beginning of `displayName`.
scopeId @3 :Id;
# ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back
# at this node, but robust code should avoid relying on this (and, in fact, group nodes are not
# listed in the outer struct's nestedNodes, since they are listed in the fields). `scopeId` is
# zero if the node has no parent, which is normally only the case with files, but should be
# allowed for any kind of node (in order to make runtime type generation easier).
nestedNodes @4 :List(NestedNode);
# List of nodes nested within this node, along with the names under which they were declared.
struct NestedNode {
name @0 :Text;
# Unqualified symbol name. Unlike Node.name, this *can* be used programmatically.
#
# (On Zooko's triangle, this is the node's petname according to its parent scope.)
id @1 :Id;
# ID of the nested node. Typically, the target node's scopeId points back to this node, but
# robust code should avoid relying on this.
}
annotations @5 :List(Annotation);
# Annotations applied to this node.
union {
# Info specific to each kind of node.
file @6 :Void;
struct :group {
dataWordCount @7 :UInt16;
# Size of the data section, in words.
pointerCount @8 :UInt16;
# Size of the pointer section, in pointers (which are one word each).
preferredListEncoding @9 :ElementSize;
# The preferred element size to use when encoding a list of this struct. If this is anything
# other than `inlineComposite` then the struct is one word or less in size and is a candidate
# for list packing optimization.
isGroup @10 :Bool;
# If true, then this "struct" node is actually not an independent node, but merely represents
# some named union or group within a particular parent struct. This node's scopeId refers
# to the parent struct, which may itself be a union/group in yet another struct.
#
# All group nodes share the same dataWordCount and pointerCount as the top-level
# struct, and their fields live in the same ordinal and offset spaces as all other fields in
# the struct.
#
# Note that a named union is considered a special kind of group -- in fact, a named union
# is exactly equivalent to a group that contains nothing but an unnamed union.
discriminantCount @11 :UInt16;
# Number of fields in this struct which are members of an anonymous union, and thus may
# overlap. If this is non-zero, then a 16-bit discriminant is present indicating which
# of the overlapping fields is active. This can never be 1 -- if it is non-zero, it must be
# two or more.
#
# Note that the fields of an unnamed union are considered fields of the scope containing the
# union -- an unnamed union is not its own group. So, a top-level struct may contain a
# non-zero discriminant count. Named unions, on the other hand, are equivalent to groups
# containing unnamed unions. So, a named union has its own independent schema node, with
# `isGroup` = true.
discriminantOffset @12 :UInt32;
# If `discriminantCount` is non-zero, this is the offset of the union discriminant, in
# multiples of 16 bits.
fields @13 :List(Field);
# Fields defined within this scope (either the struct's top-level fields, or the fields of
# a particular group; see `isGroup`).
#
# The fields are sorted by ordinal number, but note that because groups share the same
# ordinal space, the field's index in this list is not necessarily exactly its ordinal.
# On the other hand, the field's position in this list does remain the same even as the
# protocol evolves, since it is not possible to insert or remove an earlier ordinal.
# Therefore, for most use cases, if you want to identify a field by number, it may make the
# most sense to use the field's index in this list rather than its ordinal.
}
enum :group {
enumerants@14 :List(Enumerant);
# Enumerants ordered by numeric value (ordinal).
}
interface :group {
methods @15 :List(Method);
# Methods ordered by ordinal.
extends @31 :List(Id);
# Superclasses of this interface.
}
const :group {
type @16 :Type;
value @17 :Value;
}
annotation :group {
type @18 :Type;
targetsFile @19 :Bool;
targetsConst @20 :Bool;
targetsEnum @21 :Bool;
targetsEnumerant @22 :Bool;
targetsStruct @23 :Bool;
targetsField @24 :Bool;
targetsUnion @25 :Bool;
targetsGroup @26 :Bool;
targetsInterface @27 :Bool;
targetsMethod @28 :Bool;
targetsParam @29 :Bool;
targetsAnnotation @30 :Bool;
}
}
}
struct Field {
# Schema for a field of a struct.
name @0 :Text;
codeOrder @1 :UInt16;
# Indicates where this member appeared in the code, relative to other members.
# Code ordering may have semantic relevance -- programmers tend to place related fields
# together. So, using code ordering makes sense in human-readable formats where ordering is
# otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so the maximum
# value is count(members) - 1. Fields that are members of a union are only ordered relative to
# the other members of that union, so the maximum value there is count(union.members).
annotations @2 :List(Annotation);
const noDiscriminant :UInt16 = 0xffff;
discriminantValue @3 :UInt16 = Field.noDiscriminant;
# If the field is in a union, this is the value which the union's discriminant should take when
# the field is active. If the field is not in a union, this is 0xffff.
union {
slot :group {
# A regular, non-group, non-fixed-list field.
offset @4 :UInt32;
# Offset, in units of the field's size, from the beginning of the section in which the field
# resides. E.g. for a UInt32 field, multiply this by 4 to get the byte offset from the
# beginning of the data section.
type @5 :Type;
defaultValue @6 :Value;
hadExplicitDefault @10 :Bool;
# Whether the default value was specified explicitly. Non-explicit default values are always
# zero or empty values. Usually, whether the default value was explicit shouldn't matter.
# The main use case for this flag is for structs representing method parameters:
# explicitly-defaulted parameters may be allowed to be omitted when calling the method.
}
group :group {
# A group.
typeId @7 :Id;
# The ID of the group's node.
}
}
ordinal :union {
implicit @8 :Void;
explicit @9 :UInt16;
# The original ordinal number given to the field. You probably should NOT use this; if you need
# a numeric identifier for a field, use its position within the field array for its scope.
# The ordinal is given here mainly just so that the original schema text can be reproduced given
# the compiled version -- i.e. so that `capnp compile -ocapnp` can do its job.
}
}
struct Enumerant {
# Schema for member of an enum.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the enumerants were declared in the code.
# Like Struct.Field.codeOrder.
annotations @2 :List(Annotation);
}
struct Method {
# Schema for method of an interface.
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the methods were declared in the code.
# Like Struct.Field.codeOrder.
paramStructType @2 :Id;
# ID of the parameter struct type. If a named parameter list was specified in the method
# declaration (rather than a single struct parameter type) then a corresponding struct type is
# auto-generated. Such an auto-generated type will not be listed in the interface's
# `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace.
resultStructType @3 :Id;
# ID of the return struct type; similar to `paramStructType`.
annotations @4 :List(Annotation);
}
struct Type {
# Represents a type expression.
union {
# The ordinals intentionally match those of Value.
void @0 :Void;
bool @1 :Void;
int8 @2 :Void;
int16 @3 :Void;
int32 @4 :Void;
int64 @5 :Void;
uint8 @6 :Void;
uint16 @7 :Void;
uint32 @8 :Void;
uint64 @9 :Void;
float32 @10 :Void;
float64 @11 :Void;
text @12 :Void;
data @13 :Void;
list :group {
elementType @14 :Type;
}
enum :group {
typeId @15 :Id;
}
struct :group {
typeId @16 :Id;
}
interface :group {
typeId @17 :Id;
}
anyPointer @18 :Void;
}
}
struct Value {
# Represents a value, e.g. a field default value, constant value, or annotation value.
union {
# The ordinals intentionally match those of Type.
void @0 :Void;
bool @1 :Bool;
int8 @2 :Int8;
int16 @3 :Int16;
int32 @4 :Int32;
int64 @5 :Int64;
uint8 @6 :UInt8;
uint16 @7 :UInt16;
uint32 @8 :UInt32;
uint64 @9 :UInt64;
float32 @10 :Float32;
float64 @11 :Float64;
text @12 :Text;
data @13 :Data;
list @14 :AnyPointer;
enum @15 :UInt16;
struct @16 :AnyPointer;
interface @17 :Void;
# The only interface value that can be represented statically is "null", whose methods always
# throw exceptions.
anyPointer @18 :AnyPointer;
}
}
struct Annotation {
# Describes an annotation applied to a declaration. Note AnnotationNode describes the
# annotation's declaration, while this describes a use of the annotation.
id @0 :Id;
# ID of the annotation node.
value @1 :Value;
}
enum ElementSize {
# Possible element sizes for encoded lists. These correspond exactly to the possible values of
# the 3-bit element size component of a list pointer.
empty @0; # aka "void", but that's a keyword.
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
struct CodeGeneratorRequest {
nodes @0 :List(Node);
# All nodes parsed by the compiler, including for the files on the command line and their
# imports.
requestedFiles @1 :List(RequestedFile);
# Files which were listed on the command line.
struct RequestedFile {
id @0 :Id;
# ID of the file.
filename @1 :Text;
# Name of the file as it appeared on the command-line (minus the src-prefix). You may use
# this to decide where to write the output.
imports @2 :List(Import);
# List of all imported paths seen in this file.
struct Import {
id @0 :Id;
# ID of the imported file.
name @1 :Text;
# Name which *this* file used to refer to the foreign file. This may be a relative name.
# This information is provided because it might be useful for code generation, e.g. to
# generate #include directives in C++. We don't put this in Node.file because this
# information is only meaningful at compile time anyway.
#
# (On Zooko's triangle, this is the import's petname according to the importing file.)
}
}
}

View File

@@ -0,0 +1,74 @@
# addressbook_fast.pyx
# distutils: language = c++
# distutils: extra_compile_args = --std=c++11
# distutils: include_dirs = /usr/local/lib/python2.7/site-packages
# cython: c_string_type = str
# cython: c_string_encoding = default
# cython: embedsignature = True
import capnp
{%- for file in code.requestedFiles %}
import {{file.filename | replace('.', '_')}}
{% endfor %}
from capnp.includes.capnp_cpp cimport DynamicValue
from capnp.lib.capnp cimport _DynamicStructReader, _DynamicStructBuilder, _StructSchemaField, to_python_builder, to_python_reader, _to_dict, _setDynamicFieldWithField
{%- for node in code.nodes %}
{{node.schema}} = {{node.module_path}}.schema
{%- for field in node.struct.fields %}
cdef _StructSchemaField {{node.module_name}}_{{field.name}} = {{node.schema}}.fields['{{field.name}}']
{%- endfor %}
cdef class {{node.module_name}}_Reader(_DynamicStructReader):
def __init__(self, _DynamicStructReader struct):
self._init(struct.thisptr, struct._parent, struct.is_root, False)
{% for field in node.struct.fields %}
cpdef _get_{{field.name}}(self):
cdef DynamicValue.Reader temp = self.thisptr.getByField({{node.module_name}}_{{field.name}}.thisptr)
return to_python_reader(temp, self._parent)
property {{field.name}}:
def __get__(self):
return self._get_{{field.name}}()
{%- endfor %}
def to_dict(self, verbose=False):
return {
{% for field in node.struct.fields %}
'{{field.name}}': _to_dict(self.{{field.name}}, verbose),
{%- endfor %}
}
cdef class {{node.module_name}}_Builder(_DynamicStructBuilder):
def __init__(self, _DynamicStructBuilder struct):
self._init(struct.thisptr, struct._parent, struct.is_root, False)
{% for field in node.struct.fields %}
cpdef _get_{{field.name}}(self):
cdef DynamicValue.Builder temp = self.thisptr.getByField({{node.module_name}}_{{field.name}}.thisptr)
return to_python_builder(temp, self._parent)
cpdef _set_{{field.name}}(self, value):
_setDynamicFieldWithField(self.thisptr, {{node.module_name}}_{{field.name}}, value, self._parent)
property {{field.name}}:
def __get__(self):
return self._get_{{field.name}}()
def __set__(self, value):
self._set_{{field.name}}(value)
{%- endfor %}
def to_dict(self, verbose=False):
ret = {
{% for field in node.struct.fields %}
{% if field.discriminantValue == 65535 %}
'{{field.name}}': _to_dict(self.{{field.name}}, verbose),
{% endif %}
{%- endfor %}
}
{% if node.is_union %}
which = self.which()
ret[which] = getattr(self, which)
{% endif %}
return ret
capnp.register_type({{node.id}}, ({{node.module_name}}_Reader, {{node.module_name}}_Builder))
{% endfor %}

9
capnp/templates/setup.py Normal file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python
from distutils.core import setup
from Cython.Build import cythonize
import os
setup(
name="{{code.requestedFiles[0] | replace('.', '_')}}",
ext_modules=cythonize('*_capnp_cython.pyx', language="c++")
)

View File

@@ -1,3 +1,4 @@
jinja2 >= 2.7.3
cython > 0.19 cython > 0.19
setuptools >= 0.8 setuptools >= 0.8
pytest pytest

View File

@@ -53,11 +53,15 @@ setup(
name="pycapnp", name="pycapnp",
packages=["capnp"], packages=["capnp"],
version=VERSION, version=VERSION,
package_data={'capnp': ['*.pxd', '*.h', '*.capnp', 'helpers/*.pxd', 'helpers/*.h', 'includes/*.pxd', 'lib/*.pxd', 'lib/*.py', 'lib/*.pyx']}, package_data={'capnp': ['*.pxd', '*.h', '*.capnp', 'helpers/*.pxd', 'helpers/*.h', 'includes/*.pxd', 'lib/*.pxd', 'lib/*.py', 'lib/*.pyx', 'templates/*']},
ext_modules=cythonize('capnp/lib/*.pyx', language="c++"), ext_modules=cythonize('capnp/lib/*.pyx', language="c++"),
install_requires=[ install_requires=[
'jinja2 >= 2.7.3',
'cython > 0.19', 'cython > 0.19',
'setuptools >= 0.8'], 'setuptools >= 0.8'],
entry_points={
'console_scripts' : ['capnpc-cython = capnp._gen:main']
},
# PyPi info # PyPi info
description="A cython wrapping of the C++ Cap'n Proto library", description="A cython wrapping of the C++ Cap'n Proto library",
long_description=long_description, long_description=long_description,