Add Ganeti 2.9 design document
[ganeti-local] / lib / outils.py
1 #
2 #
3
4 # Copyright (C) 2012 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 """Module for object related utils."""
22
23
24 #: Supported container types for serialization/de-serialization (must be a
25 #: tuple as it's used as a parameter for C{isinstance})
26 _SEQUENCE_TYPES = (list, tuple, set, frozenset)
27
28
29 class AutoSlots(type):
30   """Meta base class for __slots__ definitions.
31
32   """
33   def __new__(mcs, name, bases, attrs):
34     """Called when a class should be created.
35
36     @param mcs: The meta class
37     @param name: Name of created class
38     @param bases: Base classes
39     @type attrs: dict
40     @param attrs: Class attributes
41
42     """
43     assert "__slots__" not in attrs, \
44       "Class '%s' defines __slots__ when it should not" % name
45
46     attrs["__slots__"] = mcs._GetSlots(attrs)
47
48     return type.__new__(mcs, name, bases, attrs)
49
50   @classmethod
51   def _GetSlots(mcs, attrs):
52     """Used to get the list of defined slots.
53
54     @param attrs: The attributes of the class
55
56     """
57     raise NotImplementedError
58
59
60 class ValidatedSlots(object):
61   """Sets and validates slots.
62
63   """
64   __slots__ = []
65
66   def __init__(self, **kwargs):
67     """Constructor for BaseOpCode.
68
69     The constructor takes only keyword arguments and will set
70     attributes on this object based on the passed arguments. As such,
71     it means that you should not pass arguments which are not in the
72     __slots__ attribute for this class.
73
74     """
75     slots = self.GetAllSlots()
76     for (key, value) in kwargs.items():
77       if key not in slots:
78         raise TypeError("Object %s doesn't support the parameter '%s'" %
79                         (self.__class__.__name__, key))
80       setattr(self, key, value)
81
82   @classmethod
83   def GetAllSlots(cls):
84     """Compute the list of all declared slots for a class.
85
86     """
87     slots = []
88     for parent in cls.__mro__:
89       slots.extend(getattr(parent, "__slots__", []))
90     return slots
91
92   def Validate(self):
93     """Validates the slots.
94
95     This method must be implemented by the child classes.
96
97     """
98     raise NotImplementedError
99
100
101 def ContainerToDicts(container):
102   """Convert the elements of a container to standard Python types.
103
104   This method converts a container with elements to standard Python types. If
105   the input container is of the type C{dict}, only its values are touched.
106   Those values, as well as all elements of input sequences, must support a
107   C{ToDict} method returning a serialized version.
108
109   @type container: dict or sequence (see L{_SEQUENCE_TYPES})
110
111   """
112   if isinstance(container, dict):
113     ret = dict([(k, v.ToDict()) for k, v in container.items()])
114   elif isinstance(container, _SEQUENCE_TYPES):
115     ret = [elem.ToDict() for elem in container]
116   else:
117     raise TypeError("Unknown container type '%s'" % type(container))
118
119   return ret
120
121
122 def ContainerFromDicts(source, c_type, e_type):
123   """Convert a container from standard python types.
124
125   This method converts a container with standard Python types to objects. If
126   the container is a dict, we don't touch the keys, only the values.
127
128   @type source: None, dict or sequence (see L{_SEQUENCE_TYPES})
129   @param source: Input data
130   @type c_type: type class
131   @param c_type: Desired type for returned container
132   @type e_type: element type class
133   @param e_type: Item type for elements in returned container (must have a
134     C{FromDict} class method)
135
136   """
137   if not isinstance(c_type, type):
138     raise TypeError("Container type '%s' is not a type" % type(c_type))
139
140   if source is None:
141     source = c_type()
142
143   if c_type is dict:
144     ret = dict([(k, e_type.FromDict(v)) for k, v in source.items()])
145   elif c_type in _SEQUENCE_TYPES:
146     ret = c_type(map(e_type.FromDict, source))
147   else:
148     raise TypeError("Unknown container type '%s'" % c_type)
149
150   return ret