def _emit_key_maybe(self, m): ann = m.has_annotation(self, annotate.key) if ann is None: return assert ann.annotation.value.is_text() allfields = [ensure_unicode(f.name) for f in self.struct.fields] # we expect keyfields to be something like "x, y, z" or "*" txt = ensure_unicode(ann.annotation.value.text.strip()) if txt == '*': fieldnames = allfields else: fieldnames = [fn.strip() for fn in txt.split(',')] # # sanity check for f in fieldnames: if f not in allfields: raise ValueError( "Error in $Py.key: the field '%s' does not exist" % f) # ns = m.code.new_scope() ns.key = ', '.join( ['self.%s' % m._convert_name(f) for f in fieldnames]) ns.w() ns.ww( """ def _key(self): return ({key},) """ ) # the trailing comma is to ensure a tuple even if there is a single field # if m.pyx: self._emit_fash_hash(m, fieldnames)
def _declare_imports(self, m): for imp in self.imports: ns = m.code.new_scope() try: filenode = m.allnodes[imp.id] except KeyError: # this means that the file was imported but not used # anywhere. Simply ignore it continue fname = ensure_unicode(filenode.displayName) ns.importname = m.register_import(fname) ns.fullpath = ensure_unicode(imp.name) if ns.fullpath == '/capnp/c++.capnp': # ignore this file as it's useless for python continue elif m.standalone: assert ns.fullpath.endswith('.capnp') ns.modname = ns.fullpath[:-6].replace('/', '.') if ns.modname.startswith('/'): ns.modname = ns.modname[1:] ns.w('import {modname} as {importname}') else: ns.pyx = m.pyx ns.w( '{importname} = __compiler.load_schema(importname="{fullpath}", pyx={pyx})' )
def _emit_fash_hash(self, m, fieldnames): # emit a specialized, fast __hash__. fields = dict([(ensure_unicode(f.name), f) for f in self.struct.fields]) m.w() with m.code.block('def __hash__(self):') as ns: ns.n = len(fieldnames) ns.w('cdef long h[{n}]') # compute the hash of each field for ns.i, fname in enumerate(fieldnames): f = fields[fname] ns.fname = m._convert_name(fname) if f.is_text(): ns.offset = f.slot.offset * f.slot.get_size() ns.w('h[{i}] = self._hash_str_text({offset})') else: ns.hash = self._fasthash_for_field(f) ns.w('h[{i}] = {hash}(self.{fname})') # # compute the hash of the whole tuple ns.w('return _hash.tuplehash(h, {n})') # # XXX this is a hack/workaround for what it looks like a Cython bug: # apparently, we need to redefine __richcmp__ together with __hash__, # else the base one is not going to be called. Moreover, for no good # reason "self" is typed as PyObject* instead of being given the # precise type, so we cast to Struct_ to force early binding ns.w() ns.ww(""" def __richcmp__(self, other, op): return (<_Struct>self)._richcmp(other, op) """)
def emit_declaration(self, m): children = m.children[self.id] for child in children: child.emit_declaration(m) # # find and register all groups having a $key annotation. We need to do # it here because we need this info when we emit the definition for # the group class for field in self.struct.fields or []: ann = m.has_annotation(field, annotate.key) if ann: if field.is_void(): # Register the fake node_group. field = m.field_override[field] assert field.is_group() group_node = m.allnodes[field.group.typeId] m.register_extra_annotation(group_node, ann) ns = m.code.new_scope() ns.name = ensure_unicode(self.compile_name(m)) ns.dotname = self.runtime_name(m) if m.pyx: ns.w("cdef class {name}(_Struct)") else: ns.w("class {name}(_Struct): pass") ns.w("{name}.__name__ = '{dotname}'") # self._emit_union_tag_declaration(m) ns.w()
def _exec(self, *cmd): #print ' '.join(cmd) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() ret = proc.wait() if ret != 0: raise CompilerError(ensure_unicode(stderr)) return stdout
def decode(self, obj): from capnpy.message import dumps from subprocess import Popen, PIPE cmd = ['capnp', 'decode', '--short', self.mod.__schema__, obj.__class__.__name__] proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) proc.stdin.write(dumps(obj)) stdout, stderr = proc.communicate() ret = proc.wait() if ret != 0: raise ValueError(stderr) return ensure_unicode(stdout.strip())
def from_group_annotation(cls, m, parent_id, field_void, annotation): """ `parent_id` is the id of the struct that contains the field which is annotated by `Py.group`. """ parent = m.allnodes[parent_id] annotation_text = ensure_unicode(annotation.value.text.strip()) # we expect arguments to be something like "x, y, z" group_field_names = [fn.strip() for fn in annotation_text.split(',')] all_fields = {ensure_unicode(f.name): f for f in parent.struct.fields} fields = [all_fields[f] for f in group_field_names] # Make sure it is bytes. displayName = parent.displayName + b'.' + field_void.name displayNamePrefixLength = len(parent.displayName) + 1 scopeId = parent_id node_id = parent_id + hash(field_void.name) % 10000 # Need a better way to get a UID. # Todo: Use proper md5 assert node_id not in m.allnodes # There shouldn't be any unions inside the group. # So do not have to deal with discriminantCount struct = cls.Struct( dataWordCount=parent.struct.dataWordCount, pointerCount=parent.struct.pointerCount, # Not sure if this is correct. preferredListEncoding=parent.struct.preferredListEncoding, isGroup=True, fields=fields, ) ret = cls( id=node_id, displayName=displayName, displayNamePrefixLength=displayNamePrefixLength, scopeId=scopeId, struct=struct, ) return ret
def _capnp_check_version(self): version = self._exec('capnp', '--version') if PY3: version = ensure_unicode(version) version = version.strip() if not version.startswith("Cap'n Proto version"): raise CompilerError("capnp version string not recognized: %s" % version) _, version = version.rsplit(' ', 1) if version < LooseVersion('0.5.0'): raise CompilerError( "The capnp executable is too old: the minimum required " "version is 0.5.0")
def _convert_name(self, name): name = ensure_unicode(name) if PY3 else name if self.convert_case: return from_camel_case(name) else: return name
def compile_name(self, m, prefix=''): if self.is_imported(m): return ensure_unicode(self.runtime_name(m, sep='.' + prefix)) return ensure_unicode(prefix + self._fullname(m, '_'))
def shortname(self, m): name = self.displayName[self.displayNamePrefixLength:] if self.is_file(): filename = ensure_unicode(self.displayName) return ensure_unicode(m.importnames[filename]) return ensure_unicode(name)
def emit(self, m): m.modname = py.path.local(ensure_unicode(self.filename)).purebasename if not PY3: m.modname = m.modname.encode('utf-8') m.tmpname = '%s_tmp' % m.modname m.code.global_scope.extname = '%s_extended' % m.modname # # some lines need to be different when in pyx mode: here we define # some global kwarg which are "turned off" when in pure python mode if m.pyx: # pyx mode m.code.global_scope.cimport = 'cimport' m.code.global_scope.cpdef = 'cpdef' m.code.global_scope.__dict__['cdef class'] = 'cdef class' else: m.code.global_scope.cimport = 'import' m.code.global_scope.cpdef = 'def' m.code.global_scope.__dict__['cdef class'] = 'class' # filenode = m.allnodes[self.id] assert filenode.is_file() m.current_scope = filenode m.w("# THIS FILE HAS BEEN GENERATED AUTOMATICALLY BY capnpy") m.w("# do not edit by hand") m.w("# generated on %s" % datetime.now().strftime("%Y-%m-%d %H:%M")) m.w("") m.w("from capnpy {cimport} ptr as _ptr") m.w("from capnpy.struct_ {cimport} Struct as _Struct") m.w("from capnpy.struct_ {cimport} check_tag as _check_tag") m.w("from capnpy.struct_ import undefined as _undefined") m.w("from capnpy.enum import enum as _enum, fill_enum as _fill_enum") m.w("from capnpy.enum {cimport} BaseEnum as _BaseEnum") m.w("from capnpy.type import Types as _Types") m.w("from capnpy.segment.builder {cimport} SegmentBuilder as _SegmentBuilder" ) m.w("from capnpy.list {cimport} List as _List") m.w("from capnpy.list {cimport} PrimitiveItemType as _PrimitiveItemType" ) m.w("from capnpy.list {cimport} BoolItemType as _BoolItemType") m.w("from capnpy.list {cimport} TextItemType as _TextItemType") m.w("from capnpy.list {cimport} StructItemType as _StructItemType") m.w("from capnpy.list {cimport} EnumItemType as _EnumItemType") m.w("from capnpy.list {cimport} VoidItemType as _VoidItemType") m.w("from capnpy.list {cimport} ListItemType as _ListItemType") m.w("from capnpy.util import text_repr as _text_repr") m.w("from capnpy.util import float32_repr as _float32_repr") m.w("from capnpy.util import float64_repr as _float64_repr") m.w("from capnpy.util import extend_module_maybe as _extend_module_maybe" ) m.w("from capnpy.util import check_version as _check_version") # if m.pyx: m.w("from capnpy cimport _hash") for t in Types.__all__: name = '%s_list_item_type' % t.name m.w("from capnpy.list {cimport} {name} as _{name}", name=name) if m.pyx and not m.standalone: # load the compiler from the outside. See the comment in # _compile_pyx for a detailed explanation m.w('from %s import __compiler, __schema__' % m.tmpname) # m.w('__capnpy_version__ = {version!r}', version=capnpy.__version__) if m.version_check: m.w('_check_version(__capnpy_version__)') else: m.w('# schema compiled with --no-version-check, skipping the call to _check_version' ) self._declare_imports(m) m.w("") # # visit the children in two passes: first the declaration, then the # definition children = m.children[filenode.id] m.w("#### FORWARD DECLARATIONS ####") m.w() for child in children: child.emit_declaration(m) m.w() m.w("#### DEFINITIONS ####") m.w() for child in children: child.emit_definition(m) # for child in children: child.emit_reference_as_child(m) # m.w() if m.standalone: m.w('_extend_module_maybe(globals(), modname=__name__)') else: m.w('_extend_module_maybe(globals(), filename=__schema__)')
def test_ensure_unicode(self): assert isinstance(ensure_unicode( 'b'), text_type) assert isinstance(ensure_unicode(u'b'), text_type) assert isinstance(ensure_unicode(b'b'), text_type)