1
# Copright 2008 Divmod, Inc. See LICENSE file for details.
4
L{epsilon.expose} is a module which allows a system that needs to expose code
5
to a network endpoint do so in a manner which only exposes methods which have
6
been explicitly designated. It provides utilities for convenient annotation
7
and lookup of exposed methods.
10
from epsilon.structlike import record
12
from epsilon.expose import Exposer, MethodNotExposed, NameRequired
14
from twisted.trial.unittest import TestCase
19
This mixin provides tests for expose, based on a parameterized base type
20
for the class which methods are being exposed on. Subclass this before
21
L{TestCase} and set L{superClass} to use this.
23
@ivar superClass: the class to be subclassed by all classes which expose
30
Create two exposers to expose methods in tests.
32
self.exposer = Exposer("test exposer")
33
self.otherExposer = Exposer("other exposer")
36
def test_exposeDocAttribute(self):
38
Creating an exposer should require a docstring explaining what it's
41
docstring = "This is my docstring."
42
exposer = Exposer(docstring)
43
self.assertEqual(exposer.__doc__, docstring)
46
def test_simpleExpose(self):
48
Creating an exposer, defining a class and exposing a method of a class
49
with that exposer, then retrieving a method of that class should result
50
in the method of that class.
52
class Foo(self.superClass):
53
def __init__(self, num):
56
@self.exposer.expose()
60
method = self.exposer.get(f, 'bar')
61
self.assertEqual(method(), 4)
64
def test_notExposed(self):
66
Creating an exposer and then attempting to retrieve a method not
67
exposed with it should result in a L{MethodNotExposed} exception.
69
class Foo(self.superClass):
73
self.assertRaises(MethodNotExposed, self.exposer.get, f, 'bar')
76
def test_differentMethodsDifferentExposers(self):
78
Methods should only be able to be retrieved with the exposer that
79
exposed them, not with any other exposer.
81
class Foo(self.superClass):
82
@self.exposer.expose()
85
@self.otherExposer.expose()
89
self.assertEqual(self.exposer.get(f, 'bar')(), 1)
90
self.assertEqual(self.otherExposer.get(f, 'baz')(), 2)
91
self.assertRaises(MethodNotExposed, self.otherExposer.get, f, 'bar')
92
self.assertRaises(MethodNotExposed, self.exposer.get, f, 'baz')
95
def test_sameMethodExposedByDifferentExposers(self):
97
If the same method is exposed by two different exposers, it should be
98
accessible by both of them.
100
class Foo(self.superClass):
101
@self.exposer.expose()
102
@self.otherExposer.expose()
106
self.assertEqual(self.exposer.get(f, 'bar')(), 4)
107
self.assertEqual(self.otherExposer.get(f, 'bar')(), 4)
110
def test_exposeWithDifferentKey(self):
112
The 'key' argument to {Exposer.expose} should change the argument to
115
class Foo(self.superClass):
116
@self.exposer.expose(key='hello')
120
self.assertEqual(self.exposer.get(f, 'hello')(), 7)
123
def test_exposeOnDifferentClass(self):
125
An exposer should only be able to retrieve a method from instances of
126
types which it has explicitly exposed methods on. Instances of
127
different types with the same method name should raise
130
class Foo(self.superClass):
131
@self.exposer.expose()
134
class Baz(self.superClass):
139
self.assertEqual(self.exposer.get(f, 'bar')(), 7)
140
self.assertRaises(MethodNotExposed, self.otherExposer.get, b, 'bar')
143
def test_exposeUnnamedNoKey(self):
145
L{Exposer.expose} raises L{NameRequired} when called without a value
146
for the C{key} parameter if it is used to decorate a non-function
150
class Foo(self.superClass):
151
@self.exposer.expose()
155
self.assertRaises(NameRequired, f)
158
def test_exposeNonMethod(self):
160
L{Exposer.expose} should work on methods which have been decorated by
161
another decorator and will therefore not result in function objects
162
when retrieved with __get__.
164
class Getter(record('function')):
165
def __get__(self, oself, type):
168
class Foo(self.superClass):
169
@self.exposer.expose(key='bar')
176
self.assertEqual(f.bar(), 7)
177
self.assertEqual(self.exposer.get(f, 'bar')(), 7)
180
def test_descriptorGetsType(self):
182
L{Exposer.get} should not interfere with the appropriate type object
183
being passed to the wrapped descriptor's C{__get__}.
186
class Getter(record('function')):
187
def __get__(self, oself, type):
190
class Foo(self.superClass):
191
@self.exposer.expose(key='bar')
196
self.exposer.get(f, 'bar')
197
self.assertEqual(types, [Foo])
200
def test_descriptorGetsSubtype(self):
202
When a descriptor is exposed through a superclass, getting it from a
203
subclass results in the subclass being passed to the C{__get__} method.
206
class Getter(record('function')):
207
def __get__(self, oself, type):
210
class Foo(self.superClass):
211
@self.exposer.expose(key='bar')
218
self.exposer.get(b, 'bar')
219
self.assertEqual(types, [Baz])
222
def test_implicitSubclassExpose(self):
224
L{Exposer.expose} should expose the given object on all subclasses.
226
class Foo(self.superClass):
227
@self.exposer.expose()
233
self.assertEqual(self.exposer.get(b, 'bar')(), 7)
236
def test_overrideDontExpose(self):
238
L{Exposer.expose} should not expose overridden methods on subclasses.
240
class Foo(self.superClass):
241
@self.exposer.expose()
248
self.assertRaises(MethodNotExposed, self.otherExposer.get, b, 'bar')
251
def test_sameKeyOnDifferentTypes(self):
253
L{Exposer.expose} should work with the same key on different types.
255
class Foo(self.superClass):
256
@self.exposer.expose()
259
class Qux(self.superClass):
260
@self.exposer.expose()
265
self.assertEqual(self.exposer.get(q, 'bar')(), 71)
266
self.assertEqual(self.exposer.get(f, 'bar')(), 17)
269
def test_overrideReExpose(self):
271
L{Exposer.expose} should expose a method on a subclass if that method
274
class Foo(self.superClass):
275
@self.exposer.expose()
279
@self.exposer.expose()
284
self.assertEqual(self.exposer.get(f, 'bar')(), 7)
285
self.assertEqual(self.exposer.get(b, 'bar')(), 8)
288
def test_deleteExposedAttribute(self):
290
When an exposed attribute is deleted from a class, it should no longer
291
be exposed; calling L{Exposer.get} should result in
294
class Foo(self.superClass):
295
@self.exposer.expose()
300
self.assertRaises(MethodNotExposed, self.otherExposer.get, f, 'bar')
304
class ExposeNewStyle(ExposeTests, TestCase):
306
All of the above functionality should work on new-style classes.
313
A dummy classic class.
317
class ExposeOldStyle(ExposeTests, TestCase):
319
All of the above functionality should work on old-style classes.