early python port of the FIT test framework (http://fit.zwiki.org/)

root / TypeAdapter.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""
Python translation of fit..
which is copyright (c) 2002 Cunningham & Cunningham, Inc.
Released under the terms of the GNU General Public License version 2 or later.
"""

import string, re
from types import *
from rexec import RExec

## Factory ##################################

def adapterOnType(fixture,atype):
    """
    Return a dummy adapter which can parse a string to this type
    """
    a = adapterFor(atype)
    a.init(fixture, atype)
    return a

def adapterOnField(fixture, fieldname, targetclass=None):
    """
    Return an adapter adapting this field of fixture's class (or
    targetclass) to the fit TypeAdapter interface (in the context of
    fixture).
    """
    if not targetclass: targetclass = fixture.__class__
    atype = type(getattr(targetclass,fieldname))
    a = adapterFor(atype)
    a.init(fixture, atype)
    a.target = fixture
    a.field = fieldname
    return a
        
def adapterOnMethod(fixture, method):
    """
    Return an adapter adapting this unbound method to the fit
    TypeAdapter interface (in the context of fixture).
    """
    a = adapterFor(UnboundMethodType)
    a.init(fixture, UnboundMethodType)
    a.target = fixture
    a.method = method
    return a
        
##public static TypeAdapter adapterFor(Class type) throws UnsupportedOperationException {
def adapterFor(atype):
    return AutoAdapter() # ha!
    if   atype == StringType: return StringAdapter()
    elif atype == IntType: return IntAdapter()
    elif atype == LongType: return LongAdapter()
    elif atype == FloatType: return FloatAdapter()
    elif atype == ComplexType: return ComplexAdapter()
    #elif atype == ListType: return ListAdapter()
    #elif atype == UnboundMethodType: return StringAdapter()
    #else: return TypeAdapter()
    elif atype == UnboundMethodType: return AutoAdapter()
    else: return AutoAdapter()

## base class ####################################

class TypeAdapter:
    """
    """
    fixture = None
    type    = None
    target  = None
    field   = None
    method  = None

    ##void init (Fixture fixture, Class type) {
    def init(self,fixture,atype):
        self.fixture = fixture
        self.type = atype

    ##public Object get() throws IllegalAccessException, InvocationTargetException {
    def get(self):
        if self.field: return getattr(self.target,self.field)
        elif self.method: return self.invoke()
        else: return None

    ##public void set(Object value) throws IllegalAccessException {
    def set(self,value):
        if self.field:
            setattr(self.target,self.field,value)

    ##public Object invoke() throws IllegalAccessException, InvocationTargetException {
    def invoke(self):
        return self.method(self.target)
    
    ##public Object parse(String s) throws Exception {
    def parse(self,s):
        return self.fixture.parse(s, self.type)
    
    ##public boolean equals(Object a, Object b) {
    def equals(self,a,b):
        # value matching
        #import sys; sys.stderr.write('comparing '+repr(a)+' \t'+repr(b))
        return a == b
        # string literal matching
        #import sys; sys.stderr.write('comparing '+repr(repr(a))+' \t'+repr(repr(b)))
        #return repr(a) == repr(b)

    ##public String toString(Object o) {
    def toString(self,o):
        #return repr(o)
        return re.sub(r"^'(.+)'$",r"\1",repr(o))

## Subclasses ##############################
# these are forgiving parsers, which may or may not be what we want

class StringAdapter(TypeAdapter):
    def parse(self,s):
        return s.strip()

class IntAdapter(TypeAdapter):
    def parse(self,s):
        return int(s)

class LongAdapter(TypeAdapter):
    def parse(self,s):
        return long(s)

class FloatAdapter(TypeAdapter):
    def parse(self,s):
        return float(s)

class ComplexAdapter(TypeAdapter):
    def parse(self,s):
        return complex(s)

#class ListAdapter(TypeAdapter):
#    pass
#    componentType = None
#    componentAdapter = None
#
#    ##void init(Fixture target, Class type) {
#    def init(self,target,type)
#        TypeAdapter.init(self,target,type)
#        self.componentType = StringType #type.getComponentType()
#        self.componentAdapter = adapterOnType(target, self.componentType)
#
#    ##public Object parse(String s) throws Exception {
#    def parse(self,s):
#        StringTokenizer t = StringTokenizer(s, ",")
#        Object array = Array.newInstance(componentType, t.countTokens())
#        for (int i=0; t.hasMoreTokens(); i++)):
#            Array.set(array, i, componentAdapter.parse(t.nextToken()))
#        return array
#
#    ##public String toString(Object o) {
#        int length = Array.getLength(o)
#        StringBuffer b = StringBuffer(5*length)
#        for (int i=0; i<length; i++)):
#            b.append(componentAdapter.toString(Array.get(o, i)))
#            if (i < (length-1))):
#                b.append(',')
#        return b.toString()
#
#    ##public boolean equals(Object a, Object b) {
#        int length = Array.getLength(a)
#        if (length != Array.getLength(b)) return false
#        for (int i=0; i<length; i++)):
#            if (!componentAdapter.equals(Array.get(a,i), Array.get(b,i))) return false
#        return true

class AutoAdapter(TypeAdapter):
    """
    This adapter returns a type based on the format of the table cell
    contents, following python's rules for literals (plus a few others).
    We could fall back on this when we don't know what type is expected.
    I am lazy and so use rexec to do the work.
    
    #Note: The RExec class can prevent code from performing unsafe
    #operations like reading or writing disk files, or using TCP/IP
    #sockets. However, it does not protect against code using
    #extremely large amounts of memory or processor time.

    As an added safety belt, strip out certain characters to discourage
    anything over-ambitious like function calls.

    It's possible this is still not safe enough, eg when invoked in the
    context of a zope process from an anonymous-writable zwiki page.
    Could use zope's RestrictedEval or just parse the data ourselves.
    Or invoke fit in a subprocess.
    
    """
#    r_eval = RExec().r_eval
    def parse(self,s):
        if s.strip().lower() == 'true':
            return 1
        elif s.strip().lower() == 'false':
            return 0
        else:
            #s = re.sub(r'[()`]',r'',s)
            #now experimenting with "float(n)" and so on
            try:
#                return self.r_eval(s)
                return eval(s)
            except SystemExit:
                return None
            except:
                return s