Doc cleanup for dbusclient.func.
[cnetworkmanager.git] / dbusclient / func.py
blobf55c4f1eba7d126ee2d408420c6f6078529ed353
1 "Converters and adaptors."
2 # TODO check spelling
4 # TODO object conversions should be more automatic
6 def seq_adaptor(item_converter):
7 "Returns a converter for a sequence, given a converter for one item."
8 return lambda seq: map(item_converter, seq)
10 def identity(x):
11 """Identity converter.
13 identity(x) == x"""
14 return x
16 def void(x):
17 """Dummy converter for functions that return nothing.
19 void(x) == None"""
20 return None
22 # random unused ideas about naming all this
23 """
24 raw value, cooked value (RV, CV)
26 basic operations:
27 wrap(RV), unwrap(CV) like marshall(CV), demarshall(RV)
29 proxy: combines both directions
30 censor, censorer: knows how to both unwrap and wrap
31 (but does not do both at a time, unlike the real one)
32 translator
34 pretty, ugly -> masker
35 """
37 def compose_converters(outer, inner):
38 """Converter composition.
40 compose_converters(f, g)(x) == f(g(x))"""
41 return lambda x: outer(inner(x))
43 class Adaptor(object):
44 """
45 An B{adaptor} groups converters for a method, property, or signal.
46 (and knows how to apply them?)
48 A B{converter} works on values, typically method arguments and return values.
49 It takes one value and returns one value (or None, for C{void}).
51 A B{biconverter} (not implemented) groups two converters that convert
52 between two types, such as escaper+unescaper, marshaller+unmarshaller,
53 opath_to_object+object_to_opath.
54 """
55 __docformat__ = "epytext en"
56 def __init__(self, ret, args, kwargs):
57 self.ret = ret
58 self.args = args
59 self.kwargs = kwargs
61 class CallableAdaptor(Adaptor):
62 "Internal, adapts arguments only"
64 def __init__(self, *args): # no kwargs yet
65 super(CallableAdaptor, self).__init__(args[0], args[1:], {})
67 @staticmethod
68 def convert_seq(seq, converters):
69 """Convert seq's items using respective converter from converters.
71 seq and converters must have same length."""
73 if len(seq) != len(converters):
74 print "SEQ:", seq
75 print "CONVERTERS:", converters
76 raise
77 return [converter(obj) for obj, converter in zip(seq, converters)]
79 @staticmethod
80 def convert_dict(dict, converter_dict):
81 """Convert dict's items using respective converter from converter_dict.
83 converter_dict must have items for all keys in dict.
84 ^ NOT, identity is used otherwise
85 """
86 retval = {}
87 for key, value in dict.iteritems():
88 converter = converter_dict.get(key, identity)
89 retval[key] = converter(value)
90 return retval
92 def adapt(self, callable):
93 def adapted_callable(*args, **kwargs):
94 args = self.convert_seq(args, self.args)
95 kwargs = self.convert_dict(kwargs, self.kwargs)
96 return callable(*args, **kwargs)
97 return adapted_callable
99 class SyncMethodAdaptor(CallableAdaptor):
100 """Adapt a method return value and arguments.
102 MethodAdaptor(retval_converter, arg1_converter, ...)
104 @classmethod
105 def kind(cls):
106 return "methods"
108 def adapt(self, callable):
109 args_adapted_callable = super(SyncMethodAdaptor, self).adapt(callable)
110 def adapted_callable(*args, **kwargs):
111 return self.ret(args_adapted_callable(*args, **kwargs))
112 return adapted_callable
114 class MethodAdaptor(CallableAdaptor):
115 """Adapt a method return value and arguments.
117 MethodAdaptor(retval_converter, arg1_converter, ...)
118 The method may be asynchronous (indicated by certain kwargs in its call)
119 in which case we do not adapt the return value.
120 (TODO possible to get the reply callback and adapt *that*?)
123 @classmethod
124 def kind(cls):
125 return "methods"
127 @staticmethod
128 def _is_async(**kwargs):
129 return kwargs.has_key('reply_handler') or \
130 kwargs.has_key('error_handler') or \
131 kwargs.has_key('ignore_reply')
133 def adapt(self, callable):
134 args_adapted_callable = super(MethodAdaptor, self).adapt(callable)
135 def adapted_callable(*args, **kwargs):
136 if self._is_async(**kwargs):
137 return args_adapted_callable(*args, **kwargs)
138 else:
139 return self.ret(args_adapted_callable(*args, **kwargs))
140 return adapted_callable
143 class PropertyAdaptor(Adaptor):
144 """Adapt a property.
146 PropertyAdaptor(get_converter [, set_converter])
148 # TODO biconverter as sole arg
149 def __init__(self, getter, setter=identity):
150 # no setter at all for read only?
151 super(PropertyAdaptor, self).__init__(getter, [setter], {})
153 @classmethod
154 def kind(cls):
155 return "properties"
157 def adapt(self, value):
158 return self.ret(value)
160 def adapt_write(self, value):
161 return self.args[0](value)
163 class SignalAdaptor(CallableAdaptor):
164 """Adapt a signal.
166 SignalAdaptor(arg1_converter, ...)
168 def __init__(self, *args): # no kwargs yet
169 super(SignalAdaptor, self).__init__(void, *args)
171 @classmethod
172 def kind(cls):
173 return "signals"
175 #FIXME duplicated in pydoc
176 MA = MethodAdaptor
177 PA = PropertyAdaptor
178 SA = SignalAdaptor