1
# Copright 2008 Divmod, Inc. See LICENSE file for details.
2
# -*- test-case-name: epsilon.test.test_expose -*-
5
This module provides L{Exposer}, a utility for creating decorators that expose
6
methods on types for a particular purpose.
8
The typical usage of this module is for an infrastructure layer (usually one
9
that allows methods to be invoked from the network, directly or indirectly) to
10
provide an explicit API for exposing those methods securely.
12
For example, a sketch of a finger protocol implementation which could use this
13
to expose the results of certain methods as finger results::
16
fingermethod = Exposer("This object exposes finger methods.")
18
class FingerProtocol(Protocol):
19
def __init__(self, fingerModel):
20
self.model = fingerModel
22
def fingerQuestionReceived(self, whichUser):
24
method = fingermethod.get(self.model, whichUser)
25
except MethodNotExposed:
26
method = lambda : "Unknown user"
30
from tx_finger import fingermethod
32
class MyFingerModel(object):
33
@fingermethod.expose("bob")
35
return "Bob is great."
37
Assuming lots of protocol code to hook everything together, this would then
38
allow you to use MyFingerModel and 'finger bob' to get the message 'Bob is
44
from types import FunctionType
47
class MethodNotExposed(Exception):
49
The requested method was not exposed for the purpose requested. More
50
specifically, L{Exposer.get} was used to retrieve a key from an object
51
which does not expose that key with that exposer.
55
class NameRequired(Exception):
57
L{Exposer.expose} was used to decorate a non-function object without having
58
a key explicitly specified.
63
class Exposer(object):
65
This is an object that can expose and retrieve methods on classes.
67
@ivar _exposed: a dict mapping exposed keys to exposed function objects.
70
def __init__(self, doc):
78
def expose(self, key=None):
80
Expose the decorated method for this L{Exposer} with the given key. A
81
method which is exposed will be able to be retrieved by this
82
L{Exposer}'s C{get} method with that key. If no key is provided, the
83
key is the method name of the exposed method.
94
@someExposer.expose('foo')
95
def unrelatedMethodName(): ...
97
@param key: a hashable object, used by L{Exposer.get} to look up the
98
decorated method later. If None, the key is the exposed method's name.
100
@return: a 1-argument callable which records its input as exposed, then
103
def decorator(function):
106
if isinstance(function, FunctionType):
107
rkey = function.__name__
110
if rkey not in self._exposed:
111
self._exposed[rkey] = []
112
self._exposed[rkey].append(function)
117
def get(self, obj, key):
119
Retrieve 'key' from an instance of a class which previously exposed it.
121
@param key: a hashable object, previously passed to L{Exposer.expose}.
123
@return: the object which was exposed with the given name on obj's key.
125
@raise MethodNotExposed: when the key in question was not exposed with
128
if key not in self._exposed:
129
raise MethodNotExposed()
130
rightFuncs = self._exposed[key]
133
for subT in inspect.getmro(T):
134
for name, value in subT.__dict__.items():
135
for rightFunc in rightFuncs:
136
if value is rightFunc:
138
raise MethodNotExposed()
139
return value.__get__(obj, T)
141
raise MethodNotExposed()