• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import itertools
8
9# pylint: disable=F0401
10import mojo.bindings.serialization as serialization
11
12
13class 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
48class 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
129def _StructInit(self, **kwargs):
130  self._fields = {}
131  for name in kwargs:
132    self.__setattr__(name, kwargs[name])
133
134
135def _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