root/galaxy-central/eggs/Cheetah-2.2.2-py2.6-macosx-10.6-universal-ucs2.egg/Cheetah/NameMapper.py @ 3

リビジョン 3, 12.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# $Id: NameMapper.py,v 1.32 2007/12/10 19:20:09 tavis_rudd Exp $
2
3"""This module supports Cheetah's optional NameMapper syntax.
4
5Overview
6================================================================================
7
8NameMapper provides a simple syntax for accessing Python data structures,
9functions, and methods from Cheetah. It's called NameMapper because it 'maps'
10simple 'names' in Cheetah templates to possibly more complex syntax in Python.
11
12Its purpose is to make working with Cheetah easy for non-programmers.
13Specifically, non-programmers using Cheetah should NOT need to be taught (a)
14what the difference is between an object and a dictionary, (b) what functions
15and methods are, and (c) what 'self' is.  A further aim (d) is to buffer the
16code in Cheetah templates from changes in the implementation of the Python data
17structures behind them.
18
19Consider this scenario:
20
21You are building a customer information system. The designers with you want to
22use information from your system on the client's website --AND-- they want to
23understand the display code and so they can maintian it themselves.
24
25You write a UI class with a 'customers' method that returns a dictionary of all
26the customer objects.  Each customer object has an 'address' method that returns
27the a dictionary with information about the customer's address.  The designers
28want to be able to access that information.
29
30Using PSP, the display code for the website would look something like the
31following, assuming your servlet subclasses the class you created for managing
32customer information:
33
34  <%= self.customer()[ID].address()['city'] %>   (42 chars)
35
36Using Cheetah's NameMapper syntax it could be any of the following:
37
38   $self.customers()[$ID].address()['city']       (39 chars)
39   --OR--                                         
40   $customers()[$ID].address()['city']           
41   --OR--                                         
42   $customers()[$ID].address().city             
43   --OR--                                         
44   $customers()[$ID].address.city               
45   --OR--                                         
46   $customers()[$ID].address.city
47   --OR--
48   $customers[$ID].address.city                   (27 chars)                   
49   
50   
51Which of these would you prefer to explain to the designers, who have no
52programming experience?  The last form is 15 characters shorter than the PSP
53and, conceptually, is far more accessible. With PHP or ASP, the code would be
54even messier than the PSP
55
56This is a rather extreme example and, of course, you could also just implement
57'$getCustomer($ID).city' and obey the Law of Demeter (search Google for more on that).
58But good object orientated design isn't the point here.
59
60Details
61================================================================================
62The parenthesized letters below correspond to the aims in the second paragraph.
63
64DICTIONARY ACCESS (a)
65---------------------
66
67NameMapper allows access to items in a dictionary using the same dotted notation
68used to access object attributes in Python.  This aspect of NameMapper is known
69as 'Unified Dotted Notation'.
70
71For example, with Cheetah it is possible to write:
72   $customers()['kerr'].address()  --OR--  $customers().kerr.address()
73where the second form is in NameMapper syntax.
74
75This only works with dictionary keys that are also valid python identifiers:
76  regex = '[a-zA-Z_][a-zA-Z_0-9]*'
77
78
79AUTOCALLING (b,d)
80-----------------
81
82NameMapper automatically detects functions and methods in Cheetah $vars and calls
83them if the parentheses have been left off. 
84
85For example if 'a' is an object, 'b' is a method
86  $a.b
87is equivalent to
88  $a.b()
89
90If b returns a dictionary, then following variations are possible
91  $a.b.c  --OR--  $a.b().c  --OR--  $a.b()['c']
92where 'c' is a key in the dictionary that a.b() returns.
93
94Further notes:
95* NameMapper autocalls the function or method without any arguments.  Thus
96autocalling can only be used with functions or methods that either have no
97arguments or have default values for all arguments.
98
99* NameMapper only autocalls functions and methods.  Classes and callable object instances
100will not be autocalled. 
101
102* Autocalling can be disabled using Cheetah's 'useAutocalling' setting.
103
104LEAVING OUT 'self' (c,d)
105------------------------
106
107NameMapper makes it possible to access the attributes of a servlet in Cheetah
108without needing to include 'self' in the variable names.  See the NAMESPACE
109CASCADING section below for details.
110
111NAMESPACE CASCADING (d)
112--------------------
113...
114
115Implementation details
116================================================================================
117
118* NameMapper's search order is dictionary keys then object attributes
119
120* NameMapper.NotFound is raised if a value can't be found for a name.
121
122Performance and the C version
123================================================================================
124
125Cheetah comes with both a C version and a Python version of NameMapper.  The C
126version is significantly faster and the exception tracebacks are much easier to
127read.  It's still slower than standard Python syntax, but you won't notice the
128difference in realistic usage scenarios.
129
130Cheetah uses the optimized C version (_namemapper.c) if it has
131been compiled or falls back to the Python version if not.
132
133Meta-Data
134================================================================================
135Authors: Tavis Rudd <tavis@damnsimple.com>,
136         Chuck Esterbrook <echuck@mindspring.com>
137Version: $Revision: 1.32 $
138Start Date: 2001/04/03
139Last Revision Date: $Date: 2007/12/10 19:20:09 $
140"""
141from __future__ import generators
142__author__ = "Tavis Rudd <tavis@damnsimple.com>," +\
143             "\nChuck Esterbrook <echuck@mindspring.com>"
144__revision__ = "$Revision: 1.32 $"[11:-2]
145import types
146from types import StringType, InstanceType, ClassType, TypeType
147from pprint import pformat
148import inspect
149import pdb
150
151_INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS = False
152_ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS = True
153__all__ = ['NotFound',
154           'hasKey',
155           'valueForKey',
156           'valueForName',
157           'valueFromSearchList',
158           'valueFromFrameOrSearchList',
159           'valueFromFrame',
160           ]
161
162if not hasattr(inspect.imp, 'get_suffixes'):
163    # This is to fix broken behavior of the inspect module under the
164    # Google App Engine, see the following issue:
165    # http://bugs.communitycheetah.org/view.php?id=10
166    setattr(inspect.imp, 'get_suffixes', lambda: [('.py', 'U', 1)])
167
168## N.B. An attempt is made at the end of this module to import C versions of
169## these functions.  If _namemapper.c has been compiled succesfully and the
170## import goes smoothly, the Python versions defined here will be replaced with
171## the C versions.
172
173class NotFound(LookupError):
174    pass
175
176def _raiseNotFoundException(key, namespace):
177    excString = "cannot find '%s'"%key
178    if _INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS:
179        excString += ' in the namespace %s'%pformat(namespace)
180    raise NotFound(excString)
181
182def _wrapNotFoundException(exc, fullName, namespace):
183    if not _ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS:
184        raise
185    else:
186        excStr = exc.args[0]
187        if excStr.find('while searching')==-1: # only wrap once!
188            excStr +=" while searching for '%s'"%fullName
189            if _INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS:
190                excStr += ' in the namespace %s'%pformat(namespace)
191            exc.args = (excStr,)
192        raise
193
194def _isInstanceOrClass(obj):
195    if type(obj) in (InstanceType, ClassType):
196        # oldstyle
197        return True
198
199    if hasattr(obj, "__class__"):
200        # newstyle
201        if hasattr(obj, 'mro'):
202            # type/class
203            return True
204        elif (hasattr(obj, 'im_func') or hasattr(obj, 'func_code') or hasattr(obj, '__self__')):
205            # method, func, or builtin func
206            return False
207        elif hasattr(obj, '__init__'):
208            # instance
209            return True
210    return False
211   
212def hasKey(obj, key):
213    """Determine if 'obj' has 'key' """
214    if hasattr(obj,'has_key') and obj.has_key(key):
215        return True
216    elif hasattr(obj, key):
217        return True
218    else:
219        return False
220
221def valueForKey(obj, key):
222    if hasattr(obj, 'has_key') and obj.has_key(key):
223        return obj[key]
224    elif hasattr(obj, key):
225        return getattr(obj, key)
226    else:
227        _raiseNotFoundException(key, obj)
228
229def _valueForName(obj, name, executeCallables=False):
230    nameChunks=name.split('.')
231    for i in range(len(nameChunks)):
232        key = nameChunks[i]
233        if hasattr(obj, 'has_key') and obj.has_key(key):
234            nextObj = obj[key]
235        else:
236            try:
237                nextObj = getattr(obj, key)
238            except AttributeError:
239                _raiseNotFoundException(key, obj)
240       
241        if executeCallables and callable(nextObj) and not _isInstanceOrClass(nextObj):
242            obj = nextObj()
243        else:
244            obj = nextObj
245    return obj
246
247def valueForName(obj, name, executeCallables=False):
248    try:
249        return _valueForName(obj, name, executeCallables)
250    except NotFound, e:
251        _wrapNotFoundException(e, fullName=name, namespace=obj)
252
253def valueFromSearchList(searchList, name, executeCallables=False):
254    key = name.split('.')[0]
255    for namespace in searchList:
256        if hasKey(namespace, key):
257            return _valueForName(namespace, name,
258                                executeCallables=executeCallables)
259    _raiseNotFoundException(key, searchList)
260
261def _namespaces(callerFrame, searchList=None):
262    yield callerFrame.f_locals
263    if searchList:
264        for namespace in searchList:
265            yield namespace
266    yield callerFrame.f_globals
267    yield __builtins__
268
269def valueFromFrameOrSearchList(searchList, name, executeCallables=False,
270                               frame=None):
271    def __valueForName():
272        try:
273            return _valueForName(namespace, name, executeCallables=executeCallables)
274        except NotFound, e:
275            _wrapNotFoundException(e, fullName=name, namespace=searchList)
276    try:
277        if not frame:
278            frame = inspect.stack()[1][0]
279        key = name.split('.')[0]
280        for namespace in _namespaces(frame, searchList):
281            if hasKey(namespace, key):
282                return __valueForName()
283        _raiseNotFoundException(key, searchList)
284    finally:
285        del frame
286
287def valueFromFrame(name, executeCallables=False, frame=None):
288    # @@TR consider implementing the C version the same way
289    # at the moment it provides a seperate but mirror implementation
290    # to valueFromFrameOrSearchList
291    try:
292        if not frame:
293            frame = inspect.stack()[1][0]
294        return valueFromFrameOrSearchList(searchList=None,
295                                          name=name,
296                                          executeCallables=executeCallables,
297                                          frame=frame)
298    finally:
299        del frame
300
301def hasName(obj, name):
302    #Not in the C version
303    """Determine if 'obj' has the 'name' """
304    key = name.split('.')[0]
305    if not hasKey(obj, key):
306        return False
307    try:
308        valueForName(obj, name)
309        return True
310    except NotFound:
311        return False
312try:
313    from _namemapper import NotFound, valueForKey, valueForName, \
314         valueFromSearchList, valueFromFrameOrSearchList, valueFromFrame
315    # it is possible with Jython or Windows, for example, that _namemapper.c hasn't been compiled
316    C_VERSION = True
317except:
318    C_VERSION = False
319
320##################################################
321## CLASSES
322
323class Mixin:
324    """@@ document me"""
325    def valueForName(self, name):
326        return valueForName(self, name)
327
328    def valueForKey(self, key):
329        return valueForKey(self, key)
330
331##################################################
332## if run from the command line ##
333
334def example():
335    class A(Mixin):
336        classVar = 'classVar val'
337        def method(self,arg='method 1 default arg'):
338            return arg
339
340        def method2(self, arg='meth 2 default arg'):
341            return {'item1':arg}
342
343        def method3(self, arg='meth 3 default'):
344            return arg
345
346    class B(A):
347        classBvar = 'classBvar val'
348
349    a = A()
350    a.one = 'valueForOne'
351    def function(whichOne='default'):
352        values = {
353            'default': 'default output',
354            'one': 'output option one',
355            'two': 'output option two'
356            }
357        return values[whichOne]
358
359    a.dic = {
360        'func': function,
361        'method': a.method3,
362        'item': 'itemval',
363        'subDict': {'nestedMethod':a.method3}
364        }
365    b = 'this is local b'
366
367    print valueForKey(a.dic,'subDict')
368    print valueForName(a, 'dic.item')
369    print valueForName(vars(), 'b')
370    print valueForName(__builtins__, 'dir')()
371    print valueForName(vars(), 'a.classVar')
372    print valueForName(vars(), 'a.dic.func', executeCallables=True)
373    print valueForName(vars(), 'a.method2.item1', executeCallables=True)
374
375if __name__ == '__main__':
376    example()
377
378
379
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。