1
2 import types, inspect
3 from zope.interface import interface, providedBy, implements
4 from foolscap.constraint import Constraint, OpenerConstraint, nothingTaster, \
5 IConstraint, UnboundedSchema, IRemoteMethodConstraint, Optional, Any
6 from foolscap.tokens import Violation, InvalidRemoteInterface
7 from foolscap.schema import addToConstraintTypeMap
8 from foolscap import ipb
9
11 """This metaclass lets RemoteInterfaces be a lot like Interfaces. The
12 methods are parsed differently (PB needs more information from them than
13 z.i extracts, and the methods can be specified with a RemoteMethodSchema
14 directly).
15
16 RemoteInterfaces can accept the following additional attribute::
17
18 __remote_name__: can be set to a string to specify the globally-unique
19 name for this interface. This should be a URL in a
20 namespace you administer. If not set, defaults to the
21 short classname.
22
23 RIFoo.names() returns the list of remote method names.
24
25 RIFoo['bar'] is still used to get information about method 'bar', however
26 it returns a RemoteMethodSchema instead of a z.i Method instance.
27
28 """
29
30 - def __init__(self, iname, bases=(), attrs=None, __module__=None):
31 if attrs is None:
32 interface.InterfaceClass.__init__(self, iname, bases, attrs,
33 __module__)
34 return
35
36
37 try:
38 rname, remote_attrs = self._parseRemoteInterface(iname, attrs)
39 except:
40 raise
41
42
43 interface.InterfaceClass.__init__(self, iname, bases, attrs,
44 __module__)
45
46
47
48
49
50
51
52
53 a = getattr(self, "_InterfaceClass__attrs")
54 a.update(remote_attrs)
55 self.__remote_name__ = rname
56
57
58 try:
59 registerRemoteInterface(self, rname)
60 except:
61 raise
62
64 remote_attrs = {}
65
66 remote_name = attrs.get("__remote_name__", iname)
67
68
69
70 if attrs.has_key("__remote_name__"):
71 del attrs["__remote_name__"]
72
73
74 names = [name for name in attrs.keys()
75 if ((type(attrs[name]) == types.FunctionType and
76 not name.startswith("_")) or
77 IConstraint.providedBy(attrs[name]))]
78
79
80
81 for name in names:
82 m = attrs[name]
83 if not IConstraint.providedBy(m):
84 m = RemoteMethodSchema(method=m)
85 m.name = name
86 m.interface = self
87 remote_attrs[name] = m
88
89
90 del attrs[name]
91
92 return remote_name, remote_attrs
93
94 RemoteInterface = RemoteInterfaceClass("RemoteInterface",
95 __module__="pb.flavors")
96
97
98
100 """Get the (one) RemoteInterface supported by the object, or None."""
101 interfaces = list(providedBy(obj))
102
103 ilist = []
104 for i in interfaces:
105 if isinstance(i, RemoteInterfaceClass):
106 if i not in ilist:
107 ilist.append(i)
108 assert len(ilist) <= 1, ("don't use multiple RemoteInterfaces! %s uses %s"
109 % (obj, ilist))
110 if ilist:
111 return ilist[0]
112 return None
113
116
117 RemoteInterfaceRegistry = {}
127
130
131
132
134 """
135 This is a constraint for a single remotely-invokable method. It gets to
136 require, deny, or impose further constraints upon a set of named
137 arguments.
138
139 This constraint is created by using keyword arguments with the same
140 names as the target method's arguments. Two special names are used:
141
142 __ignoreUnknown__: if True, unexpected argument names are silently
143 dropped. (note that this makes the schema unbounded)
144
145 __acceptUnknown__: if True, unexpected argument names are always
146 accepted without a constraint (which also makes this schema unbounded)
147
148 The remotely-accesible object's .getMethodSchema() method may return one
149 of these objects.
150 """
151
152 implements(IRemoteMethodConstraint)
153
154 taster = {}
155 opentypes = []
156 ignoreUnknown = False
157 acceptUnknown = False
158
159 name = None
160 interface = None
161
162
163 - def __init__(self, method=None, _response=None, __options=[], **kwargs):
164 if method:
165 self.initFromMethod(method)
166 return
167 self.argumentNames = []
168 self.argConstraints = {}
169 self.required = []
170 self.responseConstraint = None
171
172
173
174 if _response:
175 self.responseConstraint = IConstraint(_response)
176 self.options = {}
177
178 if kwargs.has_key("__ignoreUnknown__"):
179 self.ignoreUnknown = kwargs["__ignoreUnknown__"]
180 del kwargs["__ignoreUnknown__"]
181 if kwargs.has_key("__acceptUnknown__"):
182 self.acceptUnknown = kwargs["__acceptUnknown__"]
183 del kwargs["__acceptUnknown__"]
184
185 for argname, constraint in kwargs.items():
186 self.argumentNames.append(argname)
187 constraint = IConstraint(constraint)
188 self.argConstraints[argname] = constraint
189 if not isinstance(constraint, Optional):
190 self.required.append(argname)
191
193
194
195
196
197 names, _, _, typeList = inspect.getargspec(method)
198 if names and names[0] == 'self':
199 why = "RemoteInterface methods should not have 'self' in their argument list"
200 raise InvalidRemoteInterface(why)
201 if not names:
202 typeList = []
203
204 if typeList is None or len(names) != len(typeList):
205
206
207
208
209
210 why = "RemoteInterface methods must have default values for all their arguments"
211 raise InvalidRemoteInterface(why)
212 self.argumentNames = names
213 self.argConstraints = {}
214 self.required = []
215 for i in range(len(names)):
216 argname = names[i]
217 constraint = typeList[i]
218 if not isinstance(constraint, Optional):
219 self.required.append(argname)
220 self.argConstraints[argname] = IConstraint(constraint)
221
222
223 self.responseConstraint = IConstraint(method())
224 self.options = {}
225
226
228 if argnum >= len(self.argumentNames):
229 raise Violation("too many positional arguments: %d >= %d" %
230 (argnum, len(self.argumentNames)))
231 argname = self.argumentNames[argnum]
232 c = self.argConstraints.get(argname)
233 assert c
234 if isinstance(c, Optional):
235 c = c.constraint
236 return (True, c)
237
240 previous_args = self.argumentNames[:num_posargs]
241 for pkw in previous_kwargs:
242 assert pkw not in previous_args
243 previous_args.append(pkw)
244 if argname in previous_args:
245 raise Violation("got multiple values for keyword argument '%s'"
246 % (argname,))
247 c = self.argConstraints.get(argname)
248 if c:
249 if isinstance(c, Optional):
250 c = c.constraint
251 return (True, c)
252