1############################################################################
2# Joshua R. Boverhof, LBNL
3# See LBNLCopyright for copyright notice!
4###########################################################################
5
6import pydoc, sys, warnings
7from ZSI import TC
8
9# If function.__name__ is read-only, fail
10def _x(): return
11try:
12    _x.func_name = '_y'
13except:
14    raise RuntimeError,\
15        'use python-2.4 or later, cannot set function names in python "%s"'\
16        %sys.version
17del _x
18
19
20#def GetNil(typecode=None):
21#    """returns the nilled element, use to set an element
22#    as nilled for immutable instance.
23#    """
24#
25#    nil = TC.Nilled()
26#    if typecode is not None: nil.typecode = typecode
27#    return nil
28#
29#
30#def GetNilAsSelf(cls, typecode=None):
31#    """returns the nilled element with typecode specified,
32#    use returned instance to set this element as nilled.
33#
34#    Key Word Parameters:
35#        typecode -- use to specify a derived type or wildcard as nilled.
36#    """
37#    if typecode is not None and not isinstance(typecode, TC.TypeCode):
38#        raise TypeError, "Expecting a TypeCode instance"
39#
40#    nil = TC.Nilled()
41#    nil.typecode = typecode or cls.typecode
42#    return nil
43
44
45class pyclass_type(type):
46    """Stability: Unstable
47
48    type for pyclasses used with typecodes.  expects the typecode to
49    be available in the classdict.  creates python properties for accessing
50    and setting the elements specified in the ofwhat list, and factory methods
51    for constructing the elements.
52
53    Known Limitations:
54        1)Uses XML Schema element names directly to create method names,
55           using characters in this set will cause Syntax Errors:
56
57              (NCNAME)-(letter U digit U "_")
58
59    """
60    def __new__(cls, classname, bases, classdict):
61        """
62        """
63        #import new
64        typecode = classdict.get('typecode')
65        assert typecode is not None, 'MUST HAVE A TYPECODE.'
66
67        # Assume this means immutable type. ie. str
68        if len(bases) > 0:
69            #classdict['new_Nill'] = classmethod(GetNilAsSelf)
70            pass
71        # Assume this means mutable type. ie. ofwhat.
72        else:
73            assert hasattr(typecode, 'ofwhat'), 'typecode has no ofwhat list??'
74            assert hasattr(typecode, 'attribute_typecode_dict'),\
75                'typecode has no attribute_typecode_dict??'
76
77            #classdict['new_Nill'] = staticmethod(GetNil)
78
79            if typecode.mixed:
80                get,set = cls.__create_text_functions_from_what(typecode)
81
82                if classdict.has_key(get.__name__):
83                    raise AttributeError,\
84                        'attribute %s previously defined.' %get.__name__
85
86                if classdict.has_key(set.__name__):
87                    raise AttributeError,\
88                        'attribute %s previously defined.' %set.__name__
89
90                classdict[get.__name__] = get
91                classdict[set.__name__] = set
92
93            for what in typecode.ofwhat:
94                get,set,new_func = cls.__create_functions_from_what(what)
95
96                if classdict.has_key(get.__name__):
97                    raise AttributeError,\
98                        'attribute %s previously defined.' %get.__name__
99
100                classdict[get.__name__] = get
101                if classdict.has_key(set.__name__):
102                    raise AttributeError,\
103                        'attribute %s previously defined.' %set.__name__
104
105                classdict[set.__name__] = set
106                if new_func is not None:
107                    if classdict.has_key(new_func.__name__):
108                        raise AttributeError,\
109                            'attribute %s previously defined.' %new_func.__name__
110
111                    classdict[new_func.__name__] = new_func
112
113                assert not classdict.has_key(what.pname),\
114                    'collision with pname="%s", bail..' %what.pname
115
116                pname = what.pname
117                if pname is None and isinstance(what, TC.AnyElement): pname = 'any'
118                assert pname is not None, 'Element with no name: %s' %what
119
120                # TODO: for pname if keyword just uppercase first letter.
121                #if pydoc.Helper.keywords.has_key(pname):
122                pname = pname[0].upper() + pname[1:]
123                assert not pydoc.Helper.keywords.has_key(pname), 'unexpected keyword: %s' %pname
124
125                classdict[pname] =\
126                    property(get, set, None,
127                        'property for element (%s,%s), minOccurs="%s" maxOccurs="%s" nillable="%s"'\
128                        %(what.nspname,what.pname,what.minOccurs,what.maxOccurs,what.nillable)
129                        )
130
131        #
132        # mutable type <complexType> complexContent | modelGroup
133        # or immutable type <complexType> simpleContent (float, str, etc)
134        #
135        if hasattr(typecode, 'attribute_typecode_dict'):
136            attribute_typecode_dict = typecode.attribute_typecode_dict or {}
137            for key,what in attribute_typecode_dict.items():
138                get,set = cls.__create_attr_functions_from_what(key, what)
139                if classdict.has_key(get.__name__):
140                    raise AttributeError,\
141                        'attribute %s previously defined.' %get.__name__
142
143                if classdict.has_key(set.__name__):
144                    raise AttributeError,\
145                        'attribute %s previously defined.' %set.__name__
146
147                classdict[get.__name__] = get
148                classdict[set.__name__] = set
149
150        return type.__new__(cls,classname,bases,classdict)
151
152    def __create_functions_from_what(what):
153        if not callable(what):
154            def get(self):
155                return getattr(self, what.aname)
156
157            if what.maxOccurs > 1:
158                def set(self, value):
159                    if not (value is None or hasattr(value, '__iter__')):
160                        raise TypeError, 'expecting an iterable instance'
161                    setattr(self, what.aname, value)
162            else:
163                def set(self, value):
164                    setattr(self, what.aname, value)
165        else:
166            def get(self):
167                return getattr(self, what().aname)
168
169            if what.maxOccurs > 1:
170                def set(self, value):
171                    if not (value is None or hasattr(value, '__iter__')):
172                        raise TypeError, 'expecting an iterable instance'
173                    setattr(self, what().aname, value)
174            else:
175                def set(self, value):
176                    setattr(self, what().aname, value)
177
178        #
179        # new factory function
180        # if pyclass is None, skip
181        #
182        if not callable(what) and getattr(what, 'pyclass', None) is None:
183            new_func = None
184        elif (isinstance(what, TC.ComplexType) or
185            isinstance(what, TC.Array)):
186
187            def new_func(self):
188                '''returns a mutable type
189                '''
190                return what.pyclass()
191
192        elif not callable(what):
193
194            def new_func(self, value):
195                '''value -- initialize value
196                returns an immutable type
197                '''
198                return what.pyclass(value)
199
200        elif (issubclass(what.klass, TC.ComplexType) or
201              issubclass(what.klass, TC.Array)):
202
203            def new_func(self):
204                '''returns a mutable type or None (if no pyclass).
205                '''
206                p = what().pyclass
207                if p is None: return
208                return p()
209
210        else:
211
212            def new_func(self, value=None):
213                '''if simpleType provide initialization value, else
214                if complexType value should be left as None.
215                Parameters:
216                    value -- initialize value or None
217
218                returns a mutable instance (value is None)
219                    or an immutable instance or None (if no pyclass)
220                '''
221                p = what().pyclass
222                if p is None: return
223                if value is None: return p()
224                return p(value)
225
226        #TODO: sub all illegal characters in set
227        #    (NCNAME)-(letter U digit U "_")
228        if new_func is not None:
229            new_func.__name__ = 'new_%s' %what.pname
230        get.func_name = 'get_element_%s' %what.pname
231        set.func_name = 'set_element_%s' %what.pname
232        return get,set,new_func
233    __create_functions_from_what = staticmethod(__create_functions_from_what)
234
235    def __create_attr_functions_from_what(key, what):
236
237        def get(self):
238            '''returns attribute value for attribute %s, else None.
239            ''' %str(key)
240            return getattr(self, what.attrs_aname, {}).get(key, None)
241
242        def set(self, value):
243            '''set value for attribute %s.
244            value -- initialize value, immutable type
245            ''' %str(key)
246            if not hasattr(self, what.attrs_aname):
247                setattr(self, what.attrs_aname, {})
248            getattr(self, what.attrs_aname)[key] = value
249
250        #TODO: sub all illegal characters in set
251        #    (NCNAME)-(letter U digit U "_")
252        if type(key) in (tuple, list):
253            get.__name__ = 'get_attribute_%s' %key[1]
254            set.__name__ = 'set_attribute_%s' %key[1]
255        else:
256            get.__name__ = 'get_attribute_%s' %key
257            set.__name__ = 'set_attribute_%s' %key
258
259        return get,set
260    __create_attr_functions_from_what = \
261        staticmethod(__create_attr_functions_from_what)
262
263    def __create_text_functions_from_what(what):
264
265        def get(self):
266            '''returns text content, else None.
267            '''
268            return getattr(self, what.mixed_aname, None)
269
270        get.im_func = 'get_text'
271
272        def set(self, value):
273            '''set text content.
274            value -- initialize value, immutable type
275            '''
276            setattr(self, what.mixed_aname, value)
277
278        get.im_func = 'set_text'
279
280        return get,set
281    __create_text_functions_from_what = \
282        staticmethod(__create_text_functions_from_what)
283
284
285
286