Merge branch 'stable-2.16' into stable-2.17
[ganeti-github.git] / lib / outils.py
1 #
2 #
3
4 # Copyright (C) 2012 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Module for object related utils."""
31
32
33 #: Supported container types for serialization/de-serialization (must be a
34 #: tuple as it's used as a parameter for C{isinstance})
35 _SEQUENCE_TYPES = (list, tuple, set, frozenset)
36
37
38 class AutoSlots(type):
39 """Meta base class for __slots__ definitions.
40
41 """
42 def __new__(mcs, name, bases, attrs):
43 """Called when a class should be created.
44
45 @param mcs: The meta class
46 @param name: Name of created class
47 @param bases: Base classes
48 @type attrs: dict
49 @param attrs: Class attributes
50
51 """
52 assert "__slots__" not in attrs, \
53 "Class '%s' defines __slots__ when it should not" % name
54
55 attrs["__slots__"] = mcs._GetSlots(attrs)
56
57 return type.__new__(mcs, name, bases, attrs)
58
59 @classmethod
60 def _GetSlots(mcs, attrs):
61 """Used to get the list of defined slots.
62
63 @param attrs: The attributes of the class
64
65 """
66 raise NotImplementedError
67
68
69 class ValidatedSlots(object):
70 """Sets and validates slots.
71
72 """
73 __slots__ = []
74
75 def __init__(self, **kwargs):
76 """Constructor for BaseOpCode.
77
78 The constructor takes only keyword arguments and will set
79 attributes on this object based on the passed arguments. As such,
80 it means that you should not pass arguments which are not in the
81 __slots__ attribute for this class.
82
83 """
84 slots = self.GetAllSlots()
85 for (key, value) in kwargs.items():
86 if key not in slots:
87 raise TypeError("Object %s doesn't support the parameter '%s'" %
88 (self.__class__.__name__, key))
89 setattr(self, key, value)
90
91 @classmethod
92 def GetAllSlots(cls):
93 """Compute the list of all declared slots for a class.
94
95 """
96 slots = []
97 for parent in cls.__mro__:
98 slots.extend(getattr(parent, "__slots__", []))
99 return slots
100
101 def Validate(self):
102 """Validates the slots.
103
104 This method returns L{None} if the validation succeeds, or raises
105 an exception otherwise.
106
107 @rtype: NoneType
108 @return: L{None}, if the validation succeeds
109
110 @raise Exception: validation fails
111
112 This method must be implemented by the child classes.
113
114 """
115 raise NotImplementedError
116
117
118 def ContainerToDicts(container):
119 """Convert the elements of a container to standard Python types.
120
121 This method converts a container with elements to standard Python types. If
122 the input container is of the type C{dict}, only its values are touched.
123 Those values, as well as all elements of input sequences, must support a
124 C{ToDict} method returning a serialized version.
125
126 @type container: dict or sequence (see L{_SEQUENCE_TYPES})
127
128 """
129 if isinstance(container, dict):
130 ret = dict([(k, v.ToDict()) for k, v in container.items()])
131 elif isinstance(container, _SEQUENCE_TYPES):
132 ret = [elem.ToDict() for elem in container]
133 else:
134 raise TypeError("Unknown container type '%s'" % type(container))
135
136 return ret
137
138
139 def ContainerFromDicts(source, c_type, e_type):
140 """Convert a container from standard python types.
141
142 This method converts a container with standard Python types to objects. If
143 the container is a dict, we don't touch the keys, only the values.
144
145 @type source: None, dict or sequence (see L{_SEQUENCE_TYPES})
146 @param source: Input data
147 @type c_type: type class
148 @param c_type: Desired type for returned container
149 @type e_type: element type class
150 @param e_type: Item type for elements in returned container (must have a
151 C{FromDict} class method)
152
153 """
154 if not isinstance(c_type, type):
155 raise TypeError("Container type '%s' is not a type" % type(c_type))
156
157 if source is None:
158 source = c_type()
159
160 if c_type is dict:
161 ret = dict([(k, e_type.FromDict(v)) for k, v in source.items()])
162 elif c_type in _SEQUENCE_TYPES:
163 ret = c_type(map(e_type.FromDict, source))
164 else:
165 raise TypeError("Unknown container type '%s'" % c_type)
166
167 return ret