00001
00002
00003
00004
00005
00006
00007 import exceptions, sys, optparse, os, warnings, traceback
00008 from os.path import isfile, join, split
00009
00010
00011 import ZSI
00012 from ConfigParser import ConfigParser
00013 from ZSI.generate.wsdl2python import WriteServiceModule, ServiceDescription as wsdl2pyServiceDescription
00014 from ZSI.wstools import WSDLTools, XMLSchema
00015 from ZSI.wstools.logging import setBasicLoggerDEBUG
00016 from ZSI.generate import containers, utility
00017 from ZSI.generate.utility import NCName_to_ClassName as NC_to_CN, TextProtect
00018 from ZSI.generate.wsdl2dispatch import ServiceModuleWriter as ServiceDescription
00019 from ZSI.generate.wsdl2dispatch import WSAServiceModuleWriter as ServiceDescriptionWSA
00020
00021
00022 warnings.filterwarnings('ignore', '', exceptions.UserWarning)
00023 def SetDebugCallback(option, opt, value, parser, *args, **kwargs):
00024 setBasicLoggerDEBUG()
00025 warnings.resetwarnings()
00026
00027 def SetPyclassMetaclass(option, opt, value, parser, *args, **kwargs):
00028 """set up pyclass metaclass for complexTypes"""
00029 from ZSI.generate.containers import ServiceHeaderContainer,\
00030 TypecodeContainerBase, TypesHeaderContainer
00031
00032 TypecodeContainerBase.metaclass = kwargs['metaclass']
00033 TypesHeaderContainer.imports.append(\
00034 'from %(module)s import %(metaclass)s' %kwargs
00035 )
00036 ServiceHeaderContainer.imports.append(\
00037 'from %(module)s import %(metaclass)s' %kwargs
00038 )
00039
00040 def SetUpLazyEvaluation(option, opt, value, parser, *args, **kwargs):
00041 from ZSI.generate.containers import TypecodeContainerBase
00042 TypecodeContainerBase.lazy = True
00043
00044
00045
00046 def wsdl2py(args=None):
00047 """Utility for automatically generating client/service interface code from
00048 a wsdl definition, and a set of classes representing element declarations
00049 and type definitions. By default invoking this script produces three files,
00050 each named after the wsdl definition name, in the current working directory.
00051
00052 Generated Modules Suffix:
00053 _client.py -- client locator, rpc proxy port, messages
00054 _types.py -- typecodes representing
00055 _server.py -- server-side bindings
00056
00057 Parameters:
00058 args -- optional can provide arguments, rather than parsing
00059 command-line.
00060
00061 return:
00062 Default behavior is to return None, if args are provided then
00063 return names of the generated files.
00064
00065 """
00066 op = optparse.OptionParser(usage="USAGE: %wsdl2py [options] WSDL",
00067 description=wsdl2py.__doc__)
00068
00069
00070 op.add_option("-x", "--schema",
00071 action="store_true", dest="schema", default=False,
00072 help="process just the schema from an xsd file [no services]")
00073
00074 op.add_option("-d", "--debug",
00075 action="callback", callback=SetDebugCallback,
00076 help="debug output")
00077
00078
00079 op.add_option("-a", "--address",
00080 action="store_true", dest="address", default=False,
00081 help="ws-addressing support, must include WS-Addressing schema.")
00082
00083
00084 op.add_option("-b", "--complexType",
00085 action="callback", callback=SetPyclassMetaclass,
00086 callback_kwargs={'module':'ZSI.generate.pyclass',
00087 'metaclass':'pyclass_type'},
00088 help="add convenience functions for complexTypes, including Getters, Setters, factory methods, and properties (via metaclass). *** DONT USE WITH --simple-naming ***")
00089
00090
00091 op.add_option("-l", "--lazy",
00092 action="callback", callback=SetUpLazyEvaluation,
00093 callback_kwargs={},
00094 help="EXPERIMENTAL: recursion error solution, lazy evalution of typecodes")
00095
00096
00097 op.add_option("-w", "--twisted",
00098 action="store_true", dest='twisted', default=False,
00099 help="generate a twisted.web client/server, dependencies python>=2.4, Twisted>=2.0.0, TwistedWeb>=0.5.0")
00100
00101 op.add_option("-o", "--output-dir",
00102 action="store", dest="output_dir", default=".", type="string",
00103 help="save files in directory")
00104
00105 op.add_option("-s", "--simple-naming",
00106 action="store_true", dest="simple_naming", default=False,
00107 help="map element names directly to python attributes")
00108
00109 op.add_option("-p", "--pydoc",
00110 action="store_true", dest="pydoc", default=False,
00111 help="top-level directory for pydoc documentation.")
00112
00113
00114 is_cmdline = args is None
00115 if is_cmdline:
00116 (options, args) = op.parse_args()
00117 else:
00118 (options, args) = op.parse_args(args)
00119
00120 if len(args) != 1:
00121 print>>sys.stderr, 'Expecting a file/url as argument (WSDL).'
00122 sys.exit(os.EX_USAGE)
00123
00124 location = args[0]
00125 if options.schema is True:
00126 reader = XMLSchema.SchemaReader(base_url=location)
00127 else:
00128 reader = WSDLTools.WSDLReader()
00129
00130 load = reader.loadFromFile
00131 if not isfile(location):
00132 load = reader.loadFromURL
00133
00134 try:
00135 wsdl = load(location)
00136 except Exception, e:
00137 print >> sys.stderr, "Error loading %s: \n\t%s" % (location, e)
00138 traceback.print_exc(sys.stderr)
00139
00140 if hasattr(os, 'EX_NOINPUT'): sys.exit(os.EX_NOINPUT)
00141 sys.exit("error loading %s" %location)
00142
00143 if isinstance(wsdl, XMLSchema.XMLSchema):
00144 wsdl.location = location
00145 files = _wsdl2py(options, wsdl)
00146 else:
00147 files = _wsdl2py(options, wsdl)
00148 files.append(_wsdl2dispatch(options, wsdl))
00149
00150 if getattr(options, 'pydoc', False):
00151 _writepydoc(os.path.join('docs', 'API'), *files)
00152
00153 if is_cmdline:
00154 return
00155
00156 return files
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 def _wsdl2py(options, wsdl):
00199
00200 if options.twisted:
00201 from ZSI.generate.containers import ServiceHeaderContainer
00202 try:
00203 ServiceHeaderContainer.imports.remove('from ZSI import client')
00204 except ValueError:
00205 pass
00206 ServiceHeaderContainer.imports.append('from ZSI.twisted import client')
00207
00208
00209 if options.simple_naming:
00210
00211
00212
00213
00214
00215 containers.SetTypeNameFunc( lambda n: '%s_' %(NC_to_CN(n)) )
00216 containers.SetElementNameFunc( lambda n: '%s' %(NC_to_CN(n)) )
00217
00218 containers.ContainerBase.func_aname = lambda instnc,n: TextProtect(str(n))
00219
00220 utility.namespace_name = lambda cls, ns: utility.Namespace2ModuleName(ns)
00221
00222 files = []
00223 append = files.append
00224 if isinstance(wsdl, XMLSchema.XMLSchema):
00225 wsm = WriteServiceModule(_XMLSchemaAdapter(wsdl.location, wsdl),
00226 addressing=options.address)
00227 else:
00228 wsm = WriteServiceModule(wsdl, addressing=options.address)
00229 client_mod = wsm.getClientModuleName()
00230 client_file = join(options.output_dir, '%s.py' %client_mod)
00231 append(client_file)
00232 fd = open(client_file, 'w+')
00233 wsm.writeClient(fd)
00234 fd.close()
00235
00236 types_mod = wsm.getTypesModuleName()
00237 types_file = join(options.output_dir, '%s.py' %types_mod)
00238 append(types_file)
00239 fd = open(types_file, 'w+' )
00240 wsm.writeTypes(fd)
00241 fd.close()
00242
00243 return files
00244
00245
00246 def _wsdl2dispatch(options, wsdl):
00247 """TOOD: Remove ServiceContainer stuff, and replace with WSGI.
00248 """
00249 kw = dict()
00250 if options.twisted:
00251 from ZSI.twisted.WSresource import WSResource
00252 kw['base'] = WSResource
00253 ss = ServiceDescription(**kw)
00254 if options.address is True:
00255 raise RuntimeError, 'WS-Address w/twisted currently unsupported, edit the "factory" attribute by hand'
00256 else:
00257
00258 if options.address is True:
00259 ss = ServiceDescriptionWSA()
00260 else:
00261 ss = ServiceDescription(**kw)
00262
00263 ss.fromWSDL(wsdl)
00264 file_name = ss.getServiceModuleName()+'.py'
00265 fd = open( join(options.output_dir, file_name), 'w+')
00266 ss.write(fd)
00267 fd.close()
00268
00269 return file_name
00270
00271
00272 class _XMLSchemaAdapter:
00273 """Adapts an obj XMLSchema.XMLSchema to look like a WSDLTools.WSDL,
00274 just setting a couple attributes code expects to see.
00275 """
00276 def __init__(self, location, schema):
00277 """Parameters:
00278 location -- base location, file path
00279 schema -- XMLSchema instance
00280 """
00281 self.name = '_'.join(split(location)[-1].split('.'))
00282 self.types = {schema.targetNamespace:schema}
00283
00284
00285
00286
00287 import os, pydoc, sys, warnings, inspect
00288 import os.path
00289
00290 from distutils import log
00291 from distutils.command.build_py import build_py
00292 from distutils.util import convert_path
00293
00294
00295
00296 from ZSI.schema import ElementDeclaration, TypeDefinition
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327 def _writedoc(doc, thing, forceload=0):
00328 """Write HTML documentation to a file in the current directory.
00329 """
00330 try:
00331 object, name = pydoc.resolve(thing, forceload)
00332 page = pydoc.html.page(pydoc.describe(object), pydoc.html.document(object, name))
00333 fname = os.path.join(doc, name + '.html')
00334 file = open(fname, 'w')
00335 file.write(page)
00336 file.close()
00337 except (ImportError, pydoc.ErrorDuringImport), value:
00338 traceback.print_exc(sys.stderr)
00339 else:
00340 return name + '.html'
00341
00342
00343 def _writeclientdoc(doc, thing, forceload=0):
00344 """Write HTML documentation to a file in the current directory.
00345 """
00346 docmodule = pydoc.HTMLDoc.docmodule
00347 def strongarm(self, object, name=None, mod=None, *ignored):
00348 result = docmodule(self, object, name, mod, *ignored)
00349
00350
00351 nonmembers = []
00352 push = nonmembers.append
00353 for k,v in inspect.getmembers(object, inspect.isclass):
00354 if inspect.getmodule(v) is not object and getattr(v,'typecode',None) is not None:
00355 push('<a href="%s.html">%s</a>: pyclass alias<br/>' %(v.__name__,k))
00356
00357 result += self.bigsection('Aliases', '#ffffff', '#eeaa77', ''.join(nonmembers))
00358 return result
00359
00360 pydoc.HTMLDoc.docmodule = strongarm
00361 try:
00362 object, name = pydoc.resolve(thing, forceload)
00363 page = pydoc.html.page(pydoc.describe(object), pydoc.html.document(object, name))
00364 name = os.path.join(doc, name + '.html')
00365 file = open(name, 'w')
00366 file.write(page)
00367 file.close()
00368 except (ImportError, pydoc.ErrorDuringImport), value:
00369 log.debug(str(value))
00370
00371 pydoc.HTMLDoc.docmodule = docmodule
00372
00373 def _writetypesdoc(doc, thing, forceload=0):
00374 """Write HTML documentation to a file in the current directory.
00375 """
00376 try:
00377 object, name = pydoc.resolve(thing, forceload)
00378 name = os.path.join(doc, name + '.html')
00379 except (ImportError, pydoc.ErrorDuringImport), value:
00380 log.debug(str(value))
00381 return
00382
00383
00384 cdict = {}
00385 fdict = {}
00386 elements_dict = {}
00387 types_dict = {}
00388 for kname,klass in inspect.getmembers(thing, inspect.isclass):
00389 if thing is not inspect.getmodule(klass):
00390 continue
00391
00392 cdict[kname] = inspect.getmembers(klass, inspect.isclass)
00393 for iname,iklass in cdict[kname]:
00394 key = (kname,iname)
00395 fdict[key] = _writedoc(doc, iklass)
00396 if issubclass(iklass, ElementDeclaration):
00397
00398 try:
00399 typecode = iklass()
00400 except (AttributeError,RuntimeError), ex:
00401 elements_dict[iname] = _writebrokedoc(doc, ex, iname)
00402 continue
00403
00404 elements_dict[iname] = None
00405 if typecode.pyclass is not None:
00406 elements_dict[iname] = _writedoc(doc, typecode.pyclass)
00407
00408 continue
00409
00410 if issubclass(iklass, TypeDefinition):
00411 try:
00412 typecode = iklass(None)
00413 except (AttributeError,RuntimeError), ex:
00414 types_dict[iname] = _writebrokedoc(doc, ex, iname)
00415 continue
00416
00417 types_dict[iname] = None
00418 if typecode.pyclass is not None:
00419 types_dict[iname] = _writedoc(doc, typecode.pyclass)
00420
00421 continue
00422
00423
00424 def strongarm(self, object, name=None, mod=None, funcs={}, classes={}, *ignored):
00425 """Produce HTML documentation for a class object."""
00426 realname = object.__name__
00427 name = name or realname
00428 bases = object.__bases__
00429 object, name = pydoc.resolve(object, forceload)
00430 contents = []
00431 push = contents.append
00432 if name == realname:
00433 title = '<a name="%s">class <strong>%s</strong></a>' % (
00434 name, realname)
00435 else:
00436 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
00437 name, name, realname)
00438
00439 mdict = {}
00440 if bases:
00441 parents = []
00442 for base in bases:
00443 parents.append(self.classlink(base, object.__module__))
00444 title = title + '(%s)' % pydoc.join(parents, ', ')
00445
00446 doc = self.markup(pydoc.getdoc(object), self.preformat, funcs, classes, mdict)
00447 doc = doc and '<tt>%s<br> </tt>' % doc
00448 for iname,iclass in cdict[name]:
00449 fname = fdict[(name,iname)]
00450
00451 if elements_dict.has_key(iname):
00452 push('class <a href="%s">%s</a>: element declaration typecode<br/>'\
00453 %(fname,iname))
00454 pyclass = elements_dict[iname]
00455 if pyclass is not None:
00456 push('<ul>instance attributes:')
00457 push('<li><a href="%s">pyclass</a>: instances serializable to XML<br/></li>'\
00458 %elements_dict[iname])
00459 push('</ul>')
00460 elif types_dict.has_key(iname):
00461 push('class <a href="%s">%s</a>: type definition typecode<br/>' %(fname,iname))
00462 pyclass = types_dict[iname]
00463 if pyclass is not None:
00464 push('<ul>instance attributes:')
00465 push('<li><a href="%s">pyclass</a>: instances serializable to XML<br/></li>'\
00466 %types_dict[iname])
00467 push('</ul>')
00468 else:
00469 push('class <a href="%s">%s</a>: TODO not sure what this is<br/>' %(fname,iname))
00470
00471 contents = ''.join(contents)
00472 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
00473
00474 doclass = pydoc.HTMLDoc.docclass
00475 pydoc.HTMLDoc.docclass = strongarm
00476
00477 try:
00478 page = pydoc.html.page(pydoc.describe(object), pydoc.html.document(object, name))
00479 file = open(name, 'w')
00480 file.write(page)
00481 file.close()
00482 except (ImportError, pydoc.ErrorDuringImport), value:
00483 log.debug(str(value))
00484
00485 pydoc.HTMLDoc.docclass = doclass
00486
00487
00488
00489 def _writebrokedoc(doc, ex, name, forceload=0):
00490 try:
00491 fname = os.path.join(doc, name + '.html')
00492 page = pydoc.html.page(pydoc.describe(ex), pydoc.html.document(str(ex), fname))
00493 file = open(fname, 'w')
00494 file.write(page)
00495 file.close()
00496 except (ImportError, pydoc.ErrorDuringImport), value:
00497 log.debug(str(value))
00498
00499 return name + '.html'
00500
00501 def _writepydoc(doc, *args):
00502 """create pydoc html pages
00503 doc -- destination directory for documents
00504 *args -- modules run thru pydoc
00505 """
00506 ok = True
00507 if not os.path.isdir(doc):
00508 os.makedirs(doc)
00509
00510 if os.path.curdir not in sys.path:
00511 sys.path.append(os.path.curdir)
00512
00513 for f in args:
00514 if f.startswith('./'): f = f[2:]
00515 name = os.path.sep.join(f.strip('.py').split(os.path.sep))
00516 try:
00517 e = __import__(name)
00518 except Exception,ex:
00519 raise
00520
00521
00522
00523 if name.endswith('_client'):
00524 _writeclientdoc(doc, e)
00525 continue
00526
00527 if name.endswith('_types'):
00528 _writetypesdoc(doc, e)
00529 continue
00530
00531 try:
00532 _writedoc(doc, e)
00533 except IndexError,ex:
00534 _writebrokedoc(doc, ex, name)
00535 continue
00536
00537