Home | History | Annotate | Download | only in bindings
      1 # Copyright 2014 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """The metaclasses used by the mojo python bindings."""
      6 
      7 import itertools
      8 
      9 # pylint: disable=F0401
     10 import mojo.bindings.serialization as serialization
     11 
     12 
     13 class MojoEnumType(type):
     14   """Meta class for enumerations.
     15 
     16   Usage:
     17     class MyEnum(object):
     18       __metaclass__ = MojoEnumType
     19       VALUES = [
     20         ('A', 0),
     21         'B',
     22         ('C', 5),
     23       ]
     24 
     25       This will define a enum with 3 values, 'A' = 0, 'B' = 1 and 'C' = 5.
     26   """
     27 
     28   def __new__(mcs, name, bases, dictionary):
     29     dictionary['__slots__'] = ()
     30     dictionary['__new__'] = None
     31     for value in dictionary.pop('VALUES', []):
     32       if not isinstance(value, tuple):
     33         raise ValueError('incorrect value: %r' % value)
     34       key, enum_value = value
     35       if isinstance(key, str) and isinstance(enum_value, int):
     36         dictionary[key] = enum_value
     37       else:
     38         raise ValueError('incorrect value: %r' % value)
     39     return type.__new__(mcs, name, bases, dictionary)
     40 
     41   def __setattr__(mcs, key, value):
     42     raise AttributeError, 'can\'t set attribute'
     43 
     44   def __delattr__(mcs, key):
     45     raise AttributeError, 'can\'t delete attribute'
     46 
     47 
     48 class MojoStructType(type):
     49   """Meta class for structs.
     50 
     51   Usage:
     52     class MyStruct(object):
     53       __metaclass__ = MojoStructType
     54       DESCRIPTOR = {
     55         'constants': {
     56           'C1': 1,
     57           'C2': 2,
     58         },
     59         'enums': {
     60           'ENUM1': [
     61             ('V1', 1),
     62             'V2',
     63           ],
     64           'ENUM2': [
     65             ('V1', 1),
     66             'V2',
     67           ],
     68         },
     69         'fields': [
     70            FieldDescriptor('x', _descriptor.TYPE_INT32, 0),
     71         ],
     72       }
     73 
     74       This will define an struct, with:
     75       - 2 constants 'C1' and 'C2';
     76       - 2 enums 'ENUM1' and 'ENUM2', each of those having 2 values, 'V1' and
     77         'V2';
     78       - 1 int32 field named 'x'.
     79   """
     80 
     81   def __new__(mcs, name, bases, dictionary):
     82     dictionary['__slots__'] = ('_fields')
     83     descriptor = dictionary.pop('DESCRIPTOR', {})
     84 
     85     # Add constants
     86     dictionary.update(descriptor.get('constants', {}))
     87 
     88     # Add enums
     89     enums = descriptor.get('enums', {})
     90     for key in enums:
     91       dictionary[key] = MojoEnumType(key, (object,), { 'VALUES': enums[key] })
     92 
     93     # Add fields
     94     groups = descriptor.get('fields', [])
     95 
     96     fields = list(
     97         itertools.chain.from_iterable([group.descriptors for group in groups]))
     98     for field in fields:
     99       dictionary[field.name] = _BuildProperty(field)
    100 
    101     # Add init
    102     dictionary['__init__'] = _StructInit
    103 
    104     # Add serialization method
    105     serialization_object = serialization.Serialization(groups)
    106     def Serialize(self, handle_offset=0):
    107       return serialization_object.Serialize(self, handle_offset)
    108     dictionary['Serialize'] = Serialize
    109 
    110     def Deserialize(cls, data, handles):
    111       result = cls.__new__(cls)
    112       fields = {}
    113       serialization_object.Deserialize(fields, data, handles)
    114       result._fields = fields
    115       return result
    116     dictionary['Deserialize'] = classmethod(Deserialize)
    117 
    118     return type.__new__(mcs, name, bases, dictionary)
    119 
    120   # Prevent adding new attributes, or mutating constants.
    121   def __setattr__(mcs, key, value):
    122     raise AttributeError, 'can\'t set attribute'
    123 
    124   # Prevent deleting constants.
    125   def __delattr__(mcs, key):
    126     raise AttributeError, 'can\'t delete attribute'
    127 
    128 
    129 def _StructInit(self, **kwargs):
    130   self._fields = {}
    131   for name in kwargs:
    132     self.__setattr__(name, kwargs[name])
    133 
    134 
    135 def _BuildProperty(field):
    136   """Build the property for the given field."""
    137 
    138   # pylint: disable=W0212
    139   def Get(self):
    140     if field.name not in self._fields:
    141       self._fields[field.name] = field.GetDefaultValue()
    142     return self._fields[field.name]
    143 
    144   # pylint: disable=W0212
    145   def Set(self, value):
    146     self._fields[field.name] = field.field_type.Convert(value)
    147 
    148   return property(Get, Set)
    149