summaryrefslogtreecommitdiffstats
path: root/ZSI/generate/commands.py
blob: bf5001235d8b1a92d8106aee4555b8c919c1323c (plain) (blame)
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
############################################################################
# Joshua Boverhof<JRBoverhof@lbl.gov>, LBNL
# Monte Goode <MMGoode@lbl.gov>, LBNL
# See Copyright for copyright notice!
############################################################################

import exceptions, sys, optparse, os, warnings
from operator import xor
import ZSI
from ConfigParser import ConfigParser
from ZSI.generate.wsdl2python import WriteServiceModule, ServiceDescription
from ZSI.wstools import WSDLTools, XMLSchema
from ZSI.wstools.logging import setBasicLoggerDEBUG
from ZSI.generate import containers, utility
from ZSI.generate.utility import NCName_to_ClassName as NC_to_CN, TextProtect
from ZSI.generate.wsdl2dispatch import ServiceModuleWriter as ServiceDescription
from ZSI.generate.wsdl2dispatch import DelAuthServiceModuleWriter as DelAuthServiceDescription
from ZSI.generate.wsdl2dispatch import WSAServiceModuleWriter as ServiceDescriptionWSA
from ZSI.generate.wsdl2dispatch import DelAuthWSAServiceModuleWriter as DelAuthServiceDescriptionWSA

warnings.filterwarnings('ignore', '', exceptions.UserWarning)
def SetDebugCallback(option, opt, value, parser, *args, **kwargs):
    setBasicLoggerDEBUG()
    warnings.resetwarnings()

def SetPyclassMetaclass(option, opt, value, parser, *args, **kwargs):
    """set up pyclass metaclass for complexTypes"""
    from ZSI.generate.containers import ServiceHeaderContainer, TypecodeContainerBase, TypesHeaderContainer
    TypecodeContainerBase.metaclass = kwargs['metaclass']
    TypesHeaderContainer.imports.append(\
            'from %(module)s import %(metaclass)s' %kwargs
            )
    ServiceHeaderContainer.imports.append(\
            'from %(module)s import %(metaclass)s' %kwargs
            )

def SetUpTwistedClient(option, opt, value, parser, *args, **kwargs):
    from ZSI.generate.containers import ServiceHeaderContainer
    ServiceHeaderContainer.imports.remove('from ZSI import client')
    ServiceHeaderContainer.imports.append('from ZSI.twisted import client')
    
    
def SetUpLazyEvaluation(option, opt, value, parser, *args, **kwargs):
    from ZSI.generate.containers import TypecodeContainerBase
    TypecodeContainerBase.lazy = True
    

def formatSchemaObject(fname, schemaObj):
    """ In the case of a 'schema only' generation (-s) this creates
        a fake wsdl object that will function w/in the adapters
        and allow the generator to do what it needs to do.
    """
    
    class fake:
        pass

    f = fake()

    if fname.rfind('/'):
        tmp = fname[fname.rfind('/') + 1 :].split('.')
    else:
        tmp = fname.split('.')

    f.name  = tmp[0] + '_' + tmp[1]
    f.types = { schemaObj.targetNamespace : schemaObj }

    return f

def wsdl2py(args=None):
    """
    A utility for automatically generating client interface code from a wsdl
    definition, and a set of classes representing element declarations and
    type definitions.  This will produce two files in the current working 
    directory named after the wsdl definition name.

    eg. <definition name='SampleService'>
        SampleService.py
        SampleService_types.py
    """
    
    op = optparse.OptionParser(usage="usage: %prog [options]",
                 description=wsdl2py.__doc__)
    
    # Basic options
    op.add_option("-f", "--file",
                  action="store", dest="file", default=None, type="string",
                  help="FILE to load wsdl from")
    op.add_option("-u", "--url",
                  action="store", dest="url", default=None, type="string",
                  help="URL to load wsdl from")
    op.add_option("-x", "--schema",
                  action="store_true", dest="schema", default=False,
                  help="process just the schema from an xsd file [no services]")
    op.add_option("-d", "--debug",
                  action="callback", callback=SetDebugCallback,
                  help="debug output")
                  
    # WS Options
    op.add_option("-a", "--address",
                  action="store_true", dest="address", default=False,
                  help="ws-addressing support, must include WS-Addressing schema.")
                  
    # pyclass Metaclass 
    op.add_option("-b", "--complexType",
                  action="callback", callback=SetPyclassMetaclass, 
                  callback_kwargs={'module':'ZSI.generate.pyclass', 
                      'metaclass':'pyclass_type'},
                  help="add convenience functions for complexTypes, including Getters, Setters, factory methods, and properties (via metaclass). *** DONT USE WITH --simple-naming ***")
    
    # Lazy Evaluation of Typecodes (done at serialization/parsing when needed).
    op.add_option("-l", "--lazy",
                  action="callback", callback=SetUpLazyEvaluation, 
                  callback_kwargs={},
                  help="EXPERIMENTAL: recursion error solution, lazy evalution of typecodes")
    
    # Use Twisted
    op.add_option("-w", "--twisted",
                  action="callback", callback=SetUpTwistedClient, 
                  callback_kwargs={'module':'ZSI.generate.pyclass', 
                      'metaclass':'pyclass_type'},
                  help="generate a twisted.web client, dependencies python>=2.4, Twisted>=2.0.0, TwistedWeb>=0.5.0")
    
    # Extended generation options
    op.add_option("-e", "--extended",
                  action="store_true", dest="extended", default=False,
                  help="Do Extended code generation.")    
    op.add_option("-z", "--aname",
                  action="store", dest="aname", default=None, type="string",
                  help="pass in a function for attribute name creation")
    op.add_option("-t", "--types",
                  action="store", dest="types", default=None, type="string",
                  help="file to load types from")
    op.add_option("-o", "--output-dir",
                  action="store", dest="output_dir", default=".", type="string",
                  help="Write generated files to OUTPUT_DIR")
    op.add_option("-s", "--simple-naming",
                  action="store_true", dest="simple_naming", default=False,
                  help="Simplify generated naming.")
    op.add_option("-c", "--clientClassSuffix",
                  action="store", dest="clientClassSuffix", default=None, type="string",
                  help="Suffix to use for service client class (default \"SOAP\")")
    op.add_option("-m", "--pyclassMapModule",
                  action="store", dest="pyclassMapModule", default=None, type="string",
                  help="Python file that maps external python classes to a schema type.  The classes are used as the \"pyclass\" for that type.  The module should contain a dict() called mapping in the format: mapping = {schemaTypeName:(moduleName.py,className) }")
                  
    if args is None:
        (options, args) = op.parse_args()
    else:
        (options, args) = op.parse_args(args)

    if not xor(options.file is None, options.url is None):
        print 'Must specify either --file or --url option'
        sys.exit(os.EX_USAGE)
    
    location = options.file            
    if options.url is not None:
        location = options.url
    
    if options.schema is True:
        reader = XMLSchema.SchemaReader(base_url=location)
    else:
        reader = WSDLTools.WSDLReader()

    load = reader.loadFromFile
    if options.url is not None:
        load = reader.loadFromURL

    wsdl = None
    try:
        wsdl = load(location)
    except Exception, e:
        print "Error loading %s: \n\t%s" % (location, e)
        # exit code UNIX specific, Windows?
        if hasattr(os, 'EX_NOINPUT'): sys.exit(os.EX_NOINPUT)
        sys.exit("error loading %s" %location)
        
    if options.simple_naming:
        # Use a different client suffix
        WriteServiceModule.client_module_suffix = "_client"
        # Write messages definitions to a separate file.
        ServiceDescription.separate_messages = True
        # Use more simple type and element class names
        containers.SetTypeNameFunc( lambda n: '%s_' %(NC_to_CN(n)) )
        containers.SetElementNameFunc( lambda n: '%s' %(NC_to_CN(n)) )
        # Don't add "_" to the attribute name (remove when --aname works well)
        containers.ContainerBase.func_aname = lambda instnc,n: TextProtect(str(n))
        # write out the modules with their names rather than their number.
        utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)

    if options.clientClassSuffix:
        from ZSI.generate.containers import ServiceContainerBase
        ServiceContainerBase.clientClassSuffix = options.clientClassSuffix

    if options.schema is True:
        wsdl = formatSchemaObject(location, wsdl)

    if options.aname is not None:
        args = options.aname.rsplit('.',1)
        assert len(args) == 2, 'expecting module.function'
        # The following exec causes a syntax error.
        #exec('from %s import %s as FUNC' %(args[0],args[1]))
        assert callable(FUNC),\
            '%s must be a callable method with one string parameter' %options.aname
        from ZSI.generate.containers import TypecodeContainerBase
        TypecodeContainerBase.func_aname = staticmethod(FUNC)

    if options.pyclassMapModule != None:
        mod = __import__(options.pyclassMapModule)
        components = options.pyclassMapModule.split('.')
        for comp in components[1:]:
            mod = getattr(mod, comp)
        extPyClasses = mod.mapping
    else:
        extPyClasses = None
        
    wsm = WriteServiceModule(wsdl, addressing=options.address, do_extended=options.extended, extPyClasses=extPyClasses)
    if options.types != None:
        wsm.setTypesModuleName(options.types)
    if options.schema is False:
         fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getClientModuleName()), 'w+')
         # simple naming writes the messages to a separate file
         if not options.simple_naming:
             wsm.writeClient(fd)
         else: # provide a separate file to store messages to.
             msg_fd = open(os.path.join(options.output_dir, '%s.py' %wsm.getMessagesModuleName()), 'w+')
             wsm.writeClient(fd, msg_fd=msg_fd)
             msg_fd.close()
         fd.close()

    fd = open( os.path.join(options.output_dir, '%s.py' %wsm.getTypesModuleName()), 'w+')
    wsm.writeTypes(fd)
    fd.close()


def wsdl2dispatch(args=None):
    """
    wsdl2dispatch
    
    A utility for automatically generating service skeleton code from a wsdl
    definition.
    """
    
    op = optparse.OptionParser()
    op.add_option("-f", "--file",
                  action="store", dest="file", default=None, type="string",
                  help="file to load wsdl from")
    op.add_option("-u", "--url",
                  action="store", dest="url", default=None, type="string",
                  help="URL to load wsdl from")
    op.add_option("-a", "--address",
                  action="store_true", dest="address", default=False,
                  help="ws-addressing support, must include WS-Addressing schema.")
    op.add_option("-e", "--extended",
                  action="store_true", dest="extended", default=False,
                  help="Extended code generation.")
    op.add_option("-d", "--debug",
                  action="callback", callback=SetDebugCallback,
                  help="debug output")
    op.add_option("-t", "--types",
                  action="store", dest="types", default=None, type="string",
                  help="file to load types from")
    op.add_option("-o", "--output-dir",
                  action="store", dest="output_dir", default=".", type="string",
                  help="Write generated files to OUTPUT_DIR")
    op.add_option("-s", "--simple-naming",
                  action="store_true", dest="simple_naming", default=False,
                  help="Simplify generated naming.")
    
    if args is None:
        (options, args) = op.parse_args()
    else:
        (options, args) = op.parse_args(args)

    if options.simple_naming:
        ServiceDescription.server_module_suffix = '_interface'
        ServiceDescription.func_aname = lambda instnc,n: TextProtect(n)
        ServiceDescription.separate_messages = True
        # use module names rather than their number.
        utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)

    reader = WSDLTools.WSDLReader()
    wsdl = None
    if options.file is not None:
        wsdl = reader.loadFromFile(options.file)
    elif options.url is not None:
        wsdl = reader.loadFromURL(options.url)

    assert wsdl is not None, 'Must specify WSDL either with --file or --url'

    ss = None
    if options.address is True:
        if options.extended:
            ss = DelAuthServiceDescriptionWSA(do_extended=options.extended)
        else:
            if options.extended: 
                print >>sys.stderr, 'ERROR: --address and --extended are mutually exclusive'
                sys.exit(-1)

            ss = ServiceDescriptionWSA()
    else:
        if options.extended:
            ss = DelAuthServiceDescription(do_extended=options.extended)
        else:
            ss = ServiceDescription(do_extended=options.extended)

    ss.fromWSDL(wsdl)
    fd = open( os.path.join(options.output_dir, ss.getServiceModuleName()+'.py'), 'w+')
    ss.write(fd)
    fd.close()