blob: e976db300e8a7ace3f9e5b50445631e57559bc68 [file] [log] [blame]
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""The metaclasses used by the mojo python bindings."""
import itertools
# pylint: disable=F0401
import mojo.bindings.serialization as serialization
class MojoEnumType(type):
"""Meta class for enumerations.
Usage:
class MyEnum(object):
__metaclass__ = MojoEnumType
VALUES = [
('A', 0),
'B',
('C', 5),
]
This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5.
"""
def __new__(mcs, name, bases, dictionary):
dictionary['__slots__'] = ()
dictionary['__new__'] = None
for value in dictionary.pop('VALUES', []):
if not isinstance(value, tuple):
raise ValueError('incorrect value: %r' % value)
key, enum_value = value
if isinstance(key, str) and isinstance(enum_value, int):
dictionary[key] = enum_value
else:
raise ValueError('incorrect value: %r' % value)
return type.__new__(mcs, name, bases, dictionary)
def __setattr__(mcs, key, value):
raise AttributeError, 'can\'t set attribute'
def __delattr__(mcs, key):
raise AttributeError, 'can\'t delete attribute'
class MojoStructType(type):
"""Meta class for structs.
Usage:
class MyStruct(object):
__metaclass__ = MojoStructType
DESCRIPTOR = {
'constants': {
'C1': 1,
'C2': 2,
},
'enums': {
'ENUM1': [
('V1', 1),
'V2',
],
'ENUM2': [
('V1', 1),
'V2',
],
},
'fields': [
FieldDescriptor('x', _descriptor.TYPE_INT32, 0),
],
}
This will define an struct, with:
- 2 constants 'C1' and 'C2';
- 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and
'V2';
- 1 int32 field named 'x'.
"""
def __new__(mcs, name, bases, dictionary):
dictionary['__slots__'] = ('_fields')
descriptor = dictionary.pop('DESCRIPTOR', {})
# Add constants
dictionary.update(descriptor.get('constants', {}))
# Add enums
enums = descriptor.get('enums', {})
for key in enums:
dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] })
# Add fields
groups = descriptor.get('fields', [])
fields = list(
itertools.chain.from_iterable([group.descriptors for group in groups]))
fields.sort(key=lambda f: f.index)
for field in fields:
dictionary[field.name] = _BuildProperty(field)
# Add init
dictionary['__init__'] = _StructInit(fields)
# Add serialization method
serialization_object = serialization.Serialization(groups)
def Serialize(self, handle_offset=0):
return serialization_object.Serialize(self, handle_offset)
dictionary['Serialize'] = Serialize
def Deserialize(cls, data, handles):
result = cls.__new__(cls)
fields = {}
serialization_object.Deserialize(fields, data, handles)
result._fields = fields
return result
dictionary['Deserialize'] = classmethod(Deserialize)
dictionary['__eq__'] = _StructEq(fields)
dictionary['__ne__'] = _StructNe
return type.__new__(mcs, name, bases, dictionary)
# Prevent adding new attributes, or mutating constants.
def __setattr__(mcs, key, value):
raise AttributeError, 'can\'t set attribute'
# Prevent deleting constants.
def __delattr__(mcs, key):
raise AttributeError, 'can\'t delete attribute'
def _StructInit(fields):
def _Init(self, *args, **kwargs):
if len(args) + len(kwargs) > len(fields):
raise TypeError('__init__() takes %d argument (%d given)' %
(len(fields), len(args) + len(kwargs)))
self._fields = {}
for f, a in zip(fields, args):
self.__setattr__(f.name, a)
remaining_fields = set(x.name for x in fields[len(args):])
for name in kwargs:
if not name in remaining_fields:
if name in (x.name for x in fields[:len(args)]):
raise TypeError(
'__init__() got multiple values for keyword argument %r' % name)
raise TypeError('__init__() got an unexpected keyword argument %r' %
name)
self.__setattr__(name, kwargs[name])
return _Init
def _BuildProperty(field):
"""Build the property for the given field."""
# pylint: disable=W0212
def Get(self):
if field.name not in self._fields:
self._fields[field.name] = field.GetDefaultValue()
return self._fields[field.name]
# pylint: disable=W0212
def Set(self, value):
self._fields[field.name] = field.field_type.Convert(value)
return property(Get, Set)
def _StructEq(fields):
def _Eq(self, other):
if type(self) is not type(other):
return False
for field in fields:
if getattr(self, field.name) != getattr(other, field.name):
return False
return True
return _Eq
def _StructNe(self, other):
return not self.__eq__(other)