backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / operator / views / base.py
1 """
2 This module's purpose is to enable us to present internals of objects
3 in well-defined way to operator. To do this we can define "views"
4 on some objects. View is a definition of how to present object
5 and relations to other objects which also have their views defined.
6
7 By using views we can avoid making all interesting internal values
8 public. They will stay private and only "view" will access them
9 (think friend-class from C++)
10 """
11 import logging
12
13 from ryu.services.protocols.bgp.operator.views import fields
14
15 LOG = logging.getLogger('bgpspeaker.operator.views.base')
16
17
18 class RdyToFlattenCollection(object):
19     pass
20
21
22 class RdyToFlattenList(list, RdyToFlattenCollection):
23     pass
24
25
26 class RdyToFlattenDict(dict, RdyToFlattenCollection):
27     pass
28
29
30 class OperatorAbstractView(object):
31     """Abstract base class for operator views. It isn't meant to be
32     instantiated.
33     """
34
35     def __init__(self, obj, filter_func=None):
36         """Init
37
38         :param obj: data model for view. In other words object we
39             are creating view for. In case of ListView it should be
40             a list and in case of DictView it should be a dict.
41         :param filter_func: function to filter models
42         """
43         self._filter_func = filter_func
44         self._fields = self._collect_fields()
45         self._obj = obj
46
47     @classmethod
48     def _collect_fields(cls):
49         names = [attr for attr in dir(cls)
50                  if isinstance(getattr(cls, attr), fields.Field)]
51         return dict([(name, getattr(cls, name)) for name in names])
52
53     def combine_related(self, field_name):
54         """Combines related views. In case of DetailView it just returns
55             one-element list containing related view wrapped in
56             CombinedViewsWrapper.
57
58             In case of ListView and DictView it returns a list of related views
59             for every element of model collection also wrapped
60             in CombinedViewsWrapper.
61
62         :param field_name: field name of related view
63         :returns: vectorized form of related views. You can access them
64             as if you had only one view and you will receive flattened list
65             of responses from related views. Look at docstring of
66             CombinedViewsWrapper
67         """
68         raise NotImplementedError()
69
70     def c_rel(self, *args, **kwargs):
71         """Shortcut for combine_related. Look above
72         """
73         return self.combine_related(*args, **kwargs)
74
75     def get_field(self, field_name):
76         """Get value of data field.
77
78         :return: value of data-field of this view
79         """
80         raise NotImplementedError()
81
82     def encode(self):
83         """Representation of view which is using only python standard types.
84
85         :return: dict representation of this views data. However it
86             doesn't have to be a dict. In case of ListView it would
87             return a list. It should return wrapped types
88             for list - RdyToFlattenList, for dict - RdyToFlattenDict
89         """
90         raise NotImplementedError()
91
92     @property
93     def model(self):
94         """Getter for data model being presented by this view. Every view is
95         associated with some data model.
96
97         :return: underlaying data of this view
98         """
99         raise NotImplementedError()
100
101     def apply_filter(self, filter_func):
102         """Sets filter function to apply on model
103
104         :param filter_func: function which takes the model and returns it
105             filtered
106         """
107         self._filter_func = filter_func
108
109     def clear_filter(self):
110         self._filter_func = None
111
112
113 class OperatorDetailView(OperatorAbstractView):
114     def combine_related(self, field_name):
115         f = self._fields[field_name]
116         return CombinedViewsWrapper([f.retrieve_and_wrap(self._obj)])
117
118     def get_field(self, field_name):
119         f = self._fields[field_name]
120         return f.get(self._obj)
121
122     def encode(self):
123         encoded = {}
124         for field_name, field in self._fields.items():
125             if isinstance(field, fields.DataField):
126                 encoded[field_name] = field.get(self._obj)
127         return encoded
128
129     def rel(self, field_name):
130         f = self._fields[field_name]
131         return f.retrieve_and_wrap(self._obj)
132
133     @property
134     def model(self):
135         return self._obj
136
137
138 class OperatorListView(OperatorAbstractView):
139     def __init__(self, obj, filter_func=None):
140         assert isinstance(obj, list)
141         obj = RdyToFlattenList(obj)
142         super(OperatorListView, self).__init__(obj, filter_func)
143
144     def combine_related(self, field_name):
145         f = self._fields[field_name]
146         return CombinedViewsWrapper(RdyToFlattenList(
147             [f.retrieve_and_wrap(obj) for obj in self.model]
148         ))
149
150     def get_field(self, field_name):
151         f = self._fields[field_name]
152         return RdyToFlattenList([f.get(obj) for obj in self.model])
153
154     def encode(self):
155         encoded_list = []
156         for obj in self.model:
157             encoded_item = {}
158             for field_name, field in self._fields.items():
159                 if isinstance(field, fields.DataField):
160                     encoded_item[field_name] = field.get(obj)
161             encoded_list.append(encoded_item)
162         return RdyToFlattenList(encoded_list)
163
164     @property
165     def model(self):
166         if self._filter_func is not None:
167             return RdyToFlattenList(filter(self._filter_func, self._obj))
168         else:
169             return self._obj
170
171
172 class OperatorDictView(OperatorAbstractView):
173     def __init__(self, obj, filter_func=None):
174         assert isinstance(obj, dict)
175         obj = RdyToFlattenDict(obj)
176         super(OperatorDictView, self).__init__(obj, filter_func)
177
178     def combine_related(self, field_name):
179         f = self._fields[field_name]
180         return CombinedViewsWrapper(RdyToFlattenList(
181             [f.retrieve_and_wrap(obj) for obj in self.model.values()])
182         )
183
184     def get_field(self, field_name):
185         f = self._fields[field_name]
186         dict_to_flatten = {}
187         for key, obj in self.model.items():
188             dict_to_flatten[key] = f.get(obj)
189         return RdyToFlattenDict(dict_to_flatten)
190
191     def encode(self):
192         outer_dict_to_flatten = {}
193         for key, obj in self.model.items():
194             inner_dict_to_flatten = {}
195             for field_name, field in self._fields.items():
196                 if isinstance(field, fields.DataField):
197                     inner_dict_to_flatten[field_name] = field.get(obj)
198             outer_dict_to_flatten[key] = inner_dict_to_flatten
199         return RdyToFlattenDict(outer_dict_to_flatten)
200
201     @property
202     def model(self):
203         if self._filter_func is not None:
204             new_model = RdyToFlattenDict()
205             for k, v in self._obj.items():
206                 if self._filter_func(k, v):
207                     new_model[k] = v
208             return new_model
209         else:
210             return self._obj
211
212
213 class CombinedViewsWrapper(RdyToFlattenList):
214     """List-like wrapper for views. It provides same interface as any other
215     views but enables as to access all views in bulk.
216     It wraps and return responses from all views as a list. Be aware that
217     in case of DictViews wrapped in CombinedViewsWrapper you loose
218     information about dict keys.
219     """
220
221     def __init__(self, obj):
222         super(CombinedViewsWrapper, self).__init__(obj)
223         self._obj = obj
224
225     def combine_related(self, field_name):
226         return CombinedViewsWrapper(
227             list(_flatten(
228                 [obj.combine_related(field_name) for obj in self._obj]
229             ))
230         )
231
232     def c_rel(self, *args, **kwargs):
233         return self.combine_related(*args, **kwargs)
234
235     def encode(self):
236         return list(_flatten([obj.encode() for obj in self._obj]))
237
238     def get_field(self, field_name):
239         return list(_flatten([obj.get_field(field_name) for obj in self._obj]))
240
241     @property
242     def model(self):
243         return list(_flatten([obj.model for obj in self._obj]))
244
245     def apply_filter(self, filter_func):
246         for obj in self._obj:
247             obj.apply_filter(filter_func)
248
249     def clear_filter(self):
250         for obj in self._obj:
251             obj.clear_filter()
252
253
254 def _flatten(l, max_level=10):
255     """Generator function going deep in tree-like structures
256     (i.e. dicts in dicts or lists in lists etc.) and returning all elements as
257     a flat list. It's flattening only lists and dicts which are subclasses of
258     RdyToFlattenCollection. Regular lists and dicts are treated as a
259     single items.
260
261     :param l: some iterable to be flattened
262     :return: flattened iterator
263     """
264     if max_level >= 0:
265         _iter = l.values() if isinstance(l, dict) else l
266         for el in _iter:
267             if isinstance(el, RdyToFlattenCollection):
268                 for sub in _flatten(el, max_level=max_level - 1):
269                     yield sub
270             else:
271                 yield el
272     else:
273         yield l
274
275
276 def _create_collection_view(detail_view_class, name, encode=None,
277                             view_class=None):
278     assert issubclass(detail_view_class, OperatorDetailView)
279     class_fields = detail_view_class._collect_fields()
280     if encode is not None:
281         class_fields.update({'encode': encode})
282     return type(name, (view_class,), class_fields)
283
284
285 # function creating ListView from DetailView
286 def create_dict_view_class(detail_view_class, name):
287     encode = None
288     if 'encode' in dir(detail_view_class):
289         def encode(self):
290             dict_to_flatten = {}
291             for key, obj in self.model.items():
292                 dict_to_flatten[key] = detail_view_class(obj).encode()
293             return RdyToFlattenDict(dict_to_flatten)
294
295     return _create_collection_view(
296         detail_view_class, name, encode, OperatorDictView
297     )
298
299
300 # function creating DictView from DetailView
301 def create_list_view_class(detail_view_class, name):
302     encode = None
303     if 'encode' in dir(detail_view_class):
304         def encode(self):
305             return RdyToFlattenList([detail_view_class(obj).encode()
306                                      for obj in self.model])
307
308     return _create_collection_view(
309         detail_view_class, name, encode, OperatorListView
310     )