|
From: <bov...@us...> - 2006-10-17 22:53:38
|
Revision: 1268
http://svn.sourceforge.net/pywebsvcs/?rev=1268&view=rev
Author: boverhof
Date: 2006-10-17 15:53:19 -0700 (Tue, 17 Oct 2006)
Log Message:
-----------
M test/test_t6.py
M test/test_t4.py
-- fixed a couple failure conditions
M ZSI/dispatch.py
M ZSI/fault.py
M ZSI/client.py
M doc/c10-dispatch.tex
M doc/c08-fault.tex
-- updates to docs, and moved a little code around and minor comment changes.
Modified Paths:
--------------
trunk/zsi/ZSI/client.py
trunk/zsi/ZSI/dispatch.py
trunk/zsi/ZSI/fault.py
trunk/zsi/doc/c08-fault.tex
trunk/zsi/doc/c10-dispatch.tex
trunk/zsi/test/test_t4.py
trunk/zsi/test/test_t6.py
Modified: trunk/zsi/ZSI/client.py
===================================================================
--- trunk/zsi/ZSI/client.py 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/ZSI/client.py 2006-10-17 22:53:19 UTC (rev 1268)
@@ -67,8 +67,7 @@
class _Binding:
'''Object that represents a binding (connection) to a SOAP server.
Once the binding is created, various ways of sending and
- receiving SOAP messages are available, including a "name overloading"
- style.
+ receiving SOAP messages are available.
'''
defaultHttpTransport = httplib.HTTPConnection
defaultHttpsTransport = httplib.HTTPSConnection
@@ -444,7 +443,9 @@
class Binding(_Binding):
- '''
+ '''Object that represents a binding (connection) to a SOAP server.
+ Can be used in the "name overloading" style.
+
class attr:
gettypecode -- funcion that returns typecode from typesmodule,
can be set so can use whatever mapping you desire.
@@ -522,7 +523,7 @@
class NamedParamBinding(Binding):
- '''Like binding, except the argument list for invocation is
+ '''Like Binding, except the argument list for invocation is
named parameters.
'''
logger = _GetLogger('ZSI.client.Binding')
Modified: trunk/zsi/ZSI/dispatch.py
===================================================================
--- trunk/zsi/ZSI/dispatch.py 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/ZSI/dispatch.py 2006-10-17 22:53:19 UTC (rev 1268)
@@ -181,28 +181,7 @@
def _CGISendFault(f, **kw):
_CGISendXML(f.AsSOAP(), 500, **kw)
-def AsCGI(nsdict={}, typesmodule=None, rpc=None, modules=None):
- '''Dispatch within a CGI script.
- '''
- if os.environ.get('REQUEST_METHOD') != 'POST':
- _CGISendFault(Fault(Fault.Client, 'Must use POST'))
- return
- ct = os.environ['CONTENT_TYPE']
- try:
- if ct.startswith('multipart/'):
- cid = resolvers.MIMEResolver(ct, sys.stdin)
- xml = cid.GetSOAPPart()
- ps = ParsedSoap(xml, resolver=cid.Resolve)
- else:
- length = int(os.environ['CONTENT_LENGTH'])
- ps = ParsedSoap(sys.stdin.read(length))
- except ParseException, e:
- _CGISendFault(FaultFromZSIException(e))
- return
- _Dispatch(ps, modules, _CGISendXML, _CGISendFault, nsdict=nsdict,
- typesmodule=typesmodule, rpc=rpc)
-
class SOAPRequestHandler(BaseHTTPRequestHandler):
'''SOAP handler.
'''
@@ -247,9 +226,9 @@
docstyle=self.server.docstyle, nsdict=self.server.nsdict,
typesmodule=self.server.typesmodule, rpc=self.server.rpc)
-def AsServer(port=80, modules=None, docstyle=0, nsdict={}, typesmodule=None,
- rpc=None, **kw):
- address = ('', port)
+def AsServer(port=80, modules=None, docstyle=False, nsdict={}, typesmodule=None,
+ rpc=False, addr=''):
+ address = (addr, port)
httpd = HTTPServer(address, SOAPRequestHandler)
httpd.modules = modules
httpd.docstyle = docstyle
@@ -258,14 +237,34 @@
httpd.rpc = rpc
httpd.serve_forever()
-def AsHandler(request=None, modules=None, nsdict={}, rpc=None, **kw):
+def AsCGI(nsdict={}, typesmodule=None, rpc=False, modules=None):
+ '''Dispatch within a CGI script.
+ '''
+ if os.environ.get('REQUEST_METHOD') != 'POST':
+ _CGISendFault(Fault(Fault.Client, 'Must use POST'))
+ return
+ ct = os.environ['CONTENT_TYPE']
+ try:
+ if ct.startswith('multipart/'):
+ cid = resolvers.MIMEResolver(ct, sys.stdin)
+ xml = cid.GetSOAPPart()
+ ps = ParsedSoap(xml, resolver=cid.Resolve)
+ else:
+ length = int(os.environ['CONTENT_LENGTH'])
+ ps = ParsedSoap(sys.stdin.read(length))
+ except ParseException, e:
+ _CGISendFault(FaultFromZSIException(e))
+ return
+ _Dispatch(ps, modules, _CGISendXML, _CGISendFault, nsdict=nsdict,
+ typesmodule=typesmodule, rpc=rpc)
+
+def AsHandler(request=None, modules=None, **kw):
'''Dispatch from within ModPython.'''
ps = ParsedSoap(request)
kw['request'] = request
- _Dispatch(ps, modules, _ModPythonSendXML, _ModPythonSendFault,
- nsdict=nsdict, rpc=rpc, **kw)
-
-def AsJonPy(nsdict={}, typesmodule=None, rpc=None, modules=None, request=None, **kw):
+ _Dispatch(ps, modules, _ModPythonSendXML, _ModPythonSendFault, **kw)
+
+def AsJonPy(request=None, modules=None, **kw):
'''Dispatch within a jonpy CGI/FastCGI script.
'''
@@ -285,8 +284,7 @@
except ParseException, e:
_JonPySendFault(FaultFromZSIException(e), **kw)
return
- _Dispatch(ps, modules, _JonPySendXML, _JonPySendFault, nsdict=nsdict,
- typesmodule=typesmodule, rpc=rpc, **kw)
+ _Dispatch(ps, modules, _JonPySendXML, _JonPySendFault, **kw)
if __name__ == '__main__': print _copyright
Modified: trunk/zsi/ZSI/fault.py
===================================================================
--- trunk/zsi/ZSI/fault.py 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/ZSI/fault.py 2006-10-17 22:53:19 UTC (rev 1268)
@@ -47,7 +47,9 @@
self.any = detail
ZSIHeaderDetail.typecode =\
- Struct(ZSIHeaderDetail, [AnyElement(aname='any')], pname=(ZSI_SCHEMA_URI, 'detail'))
+ Struct(ZSIHeaderDetail,
+ [AnyElement(aname='any', minOccurs=0, maxOccurs=UNBOUNDED)],
+ pname=(ZSI_SCHEMA_URI, 'detail'))
class ZSIFaultDetailTypeCode(ElementDeclaration, Struct):
@@ -132,8 +134,8 @@
actor=None, detail=None, headerdetail=None):
if detail is not None and type(detail) not in _seqtypes:
detail = (detail,)
- #if headerdetail is not None and type(headerdetail) not in _seqtypes:
- # headerdetail = (headerdetail,)
+ if headerdetail is not None and type(headerdetail) not in _seqtypes:
+ headerdetail = (headerdetail,)
self.code, self.string, self.actor, self.detail, self.headerdetail = \
code, string, actor, detail, headerdetail
ZSIException.__init__(self, code, string, actor, detail, headerdetail)
Modified: trunk/zsi/doc/c08-fault.tex
===================================================================
--- trunk/zsi/doc/c08-fault.tex 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/doc/c08-fault.tex 2006-10-17 22:53:19 UTC (rev 1268)
@@ -62,13 +62,10 @@
A text string holding the value of the SOAP \code{faultstring} element.
\end{memberdesc}
-\begin{methoddesc}{AsSOAP}{\optional{output\optional{, **kw}}}
+\begin{methoddesc}{AsSOAP}{\optional{, **kw}}
This method serializes the \class{Fault} object into a SOAP message.
-If the \var{output} parameter is not specified or \constant{None}, the
-message is returned as a string.
-Any other keyword arguments are passed to the \class{SoapWriter} constructor.
-Otherwise \method{AsSOAP()} will call \code{\var{output}.write()} as needed
-to output the message.
+The message is returned as a string.
+Any keyword arguments are passed to the \class{SoapWriter} constructor.
\versionadded{1.1; the old \method{AsSoap()} method is still available}
\end{methoddesc}
@@ -83,8 +80,8 @@
\end{methoddesc}
\begin{methoddesc}{serialize}{sw}
-This method outputs the fault object onto the \var{sw} object,
-which must support a \method{write()} method.
+This method outputs the fault object onto the \var{sw} object, which is a
+\class{SoapWriter} instance.
\end{methoddesc}
Some convenience functions are available to create a \class{Fault}
Modified: trunk/zsi/doc/c10-dispatch.tex
===================================================================
--- trunk/zsi/doc/c10-dispatch.tex 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/doc/c10-dispatch.tex 2006-10-17 22:53:19 UTC (rev 1268)
@@ -33,29 +33,50 @@
\section{Dispatching}
-The \module{ZSI.dispatch} module allows you to expose Python functions as a
-web service.
-The module provides the infrastructure to parse the request, dispatch
-to the appropriate handler, and then serialize any return value
-back to the client.
-The value returned by the function will be serialized back to the client.
-To return multiple values, return a list.
+The \module{ZSI.dispatch} module allows you to expose Python functions as a web
+service. The module provides the infrastructure to parse the request, dispatch
+to the appropriate handler, and then serialize any return value back to the
+client. The value returned by the function will be serialized back to the
+client. If an exception occurs, a SOAP fault will be sent back to the client.
-If an exception occurs, a SOAP fault will be sent back to the client.
+\subsection{Dispatch Behaviors} By default the callback is invoked with the
+pyobj representation of the body root element, and it is expected to return a
+self-describing request (w/typecode). Parsing is done via a typecode from
+typesmodule, or Any. Other keyword options are available in dispatch mechanisms
+(see below) that result in different behavior.
+\subsubsection{rpc} An rpc service will ignore the body root (RPC Wrapper) of
+the request, and parse all "parts" of message via individual typecodes. The
+callback function is expected to return the parts of the message in a dict or a
+list. The dispatch mechanism will try to serialize it as a Struct but if this
+is not possible it will be serialized as an Array. Parsing done via a typecode
+from typesmodule, or Any. Not compatible with \var{docstyle}.
+
+\subsubsection{docstyle} Callback is invoked with a ParsedSoap instance
+representing the request, and the return value is serialized with an XML
+typecode (DOM). The result in wrapped as an rpc-style message, with
+\emph{Response} appended to the request wrapper. Not compatible with \var{rpc}.
+
+\subsection{Special Modules} These are keyword options available to all
+dispatch mechansism (see below).
+
+\subsubsection{modules}{Dispatch is based solely on the name of the root element in the
+incoming SOAP request; the request URL is ignored. These modules will be search
+for a matching function. If no modules are specified, only the
+\module{__main__} module will be searched.}
+
+\subsubsection{typesmodule}{Used for parsing. This module should contain class
+definitions with the \code{typecode} attribute set to a \class{TypeCode}
+instance. By default, a class definition matching the root element name will be
+retrieved or the Any typecode will be used. If using \emph{rpc}, each child of
+the root element will be used to retrieve a class definition of the same name.}
+
+\subsection{Dispatch Mechanisms}
Three dispatch mechanisms are provided: one supports standard CGI
scripts, one runs a dedicated server based on the
\module{BaseHTTPServer} module, and the third uses the JonPY package,
\url{http://jonpy.sourceforge.net}, to support FastCGI.
-\begin{methoddesc}{AsCGI}{\optional{module_list}}
-This method parses the CGI input and invokes a function that has the
-same name as the top-level SOAP request element.
-The optional \code{module_list} parameter can specify a list of modules
-(already imported) to search for functions.
-If no modules are specified, only the \module{__main__} module will be searched.
-\end{methoddesc}
-
\begin{methoddesc}{AsServer}{\optional{**keywords}}
This creates a \class{HTTPServer} object with a request handler that only
supports the ``POST'' method.
@@ -66,28 +87,84 @@
The following keyword arguments may be used:
\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
-\lineiii{\code{docstyle}}{\code{0}}{If true, then all methods are
-invoked with a single argument, the unparsed body of the SOAP message.}
+\lineiii{\code{port}}{\code{80}}{Port to listen on.}
+\lineiii{\code{addr}}{\code{''}}{Address to listen on.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
functions that can be invoked.}
-\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the
- SOAP \code{Envelope}}
-\lineiii{\code{port}}{\code{80}}{Port to listen on.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
\end{tableiii}
\end{methoddesc}
-\begin{methoddesc}{AsJonPy}{request=req\optional{, **keywords}}
+\begin{methoddesc}{AsCGI}{\optional{**keywords}}
+This method parses the CGI input and invokes a function that has the
+same name as the top-level SOAP request element.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+
+\end{methoddesc}
+
+
+
+\begin{methoddesc}{AsHandler}{request=None\optional{, **keywords}}
+
This method is used within a JonPY handler to do dispatch.
The following keyword arguments may be used:
\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
-\lineiii{\code{request}}{\code{(__main__,)}}{List of modules containing
+\lineiii{\code{request}}{\code{None}}{modpython HTTPRequest instance.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
functions that can be invoked.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
\end{tableiii}
+\end{methoddesc}
+
+\begin{methoddesc}{AsJonPy}{request=None\optional{, **keywords}}
+
+This method is used within a JonPY handler to do dispatch.
+
+The following keyword arguments may be used:
+
+\begin{tableiii}{l|c|p{30em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{request}}{\code{None}}{jonpy Request instance.}
+\lineiii{\code{modules}}{\code{(__main__,)}}{List of modules containing
+functions that can be invoked.}
+\lineiii{\code{docstyle}}{\code{False}}{Exhibit the \emph{docstyle} behavior.}
+\lineiii{\code{rpc}}{\code{False}}{Exhibit the \emph{rpc} behavior.}
+\lineiii{\code{typesmodule}}{\code{(__main__,)}}{This module is used for
+parsing, it contains class definitions that specify the \code{typecode}
+attribute.}
+\lineiii{\code{nsdict}}{\code{\{\}}}{Namespace dictionary to send in the SOAP
+\code{Envelope}}
+\end{tableiii}
+
+
The following code shows a sample use:
\begin{verbatim}
@@ -104,6 +181,8 @@
\end{methoddesc}
+\subsection{Other Dispatch Stuff}
+
\begin{methoddesc}{GetClientBinding}{}
More sophisticated scripts may want to use access the client binding object,
which encapsulates all information about the client invoking the script.
@@ -140,6 +219,7 @@
This is most useful when \method{AsCGI()} is used.
\end{memberdesc}
+
\section{The \module{client} module --- sending SOAP messages}
\ZSI{} includes a module to connect to a SOAP server over HTTP, send requests,
@@ -149,7 +229,9 @@
It must be explicitly imported, as in
\samp{from ZSI.client import AUTH,Binding}.
-\begin{classdesc}{Binding}{\optional{**keywords}}
+\subsection{_Binding}
+
+\begin{classdesc}{_Binding}{\optional{**keywords}}
This class encapsulates a connection to a server, known as a \emph{binding}.
A single binding may be used for multiple RPC calls.
Between calls, modifiers may be used to change the URL being posted to,
@@ -177,18 +259,20 @@
\lineiii{\code{transport}}{HTTPConnection/HTTPSConnection}{transport class}
\lineiii{\code{transdict}}{\{\}}{keyword arguments for connection initialization}
\lineiii{\code{url}}{n/a}{URL to post to.}
+\lineiii{\code{wsAddressURI}}{None}{URI, identifies the WS-Address specification
+to use. By default it's not used.}
+\lineiii{\code{sig_handler}}{None}{XML Signature handler, must sign and verify.}
\end{tableiii}
If using SSL, the \code{cert_file} and \code{key_file} keyword parameters may
-also be used.
-For details see the documentation for the \module{httplib} module.
+also be used. For details see the documentation for the \module{httplib}
+module.
\end{classdesc}
-Once a \class{Binding} object has been created, the following modifiers are
-available.
-All of them return the binding object, so that multiple modifiers can
-be chained together.
+Once a \class{_Binding} object has been created, the following modifiers are
+available. All of them return the binding object, so that multiple modifiers
+can be chained together.
\begin{methoddesc}{AddHeader}{header, value}
Output the specified \code{header} and \code{value} with the HTTP
@@ -317,15 +401,42 @@
A text string containing the HTTP reply text.
\end{memberdesc}
-Finally, if an attribute is fetched other than one of those described
-above, it is taken to be the \code{opname} of a remote procedure,
-and a callable object is returned.
-This object dynamically parses its arguments, receives the reply, and
-parses that.
+\subsection{Binding}
+If an attribute is fetched other than one of those described in
+\class{_Binding}, it is taken to be the \code{opname} of a remote procedure, and
+a callable object is returned. This object dynamically parses its arguments,
+receives the reply, and parses that.
-\begin{methoddesc}{opname}{args...}
-Using this shortcut requires that the \method{SetURL()} was invoked first.
-This method is then equivalent to:
-\samp{RPC(None, opname, tuple(args), TC.Any())}
+\begin{classdesc}{Binding}{\optional{**keywords}}
+For other keyword arguments see \class{_Binding}.
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{typesmodule}}{\code{None}}{See explanation in Dispatching}
+\end{tableiii}
+\end{classdesc}
+
+\begin{methoddesc}{opname}{*args}
+Using this shortcut requires that the \var{url} attribute is set, either
+throught the constructor or \method{SetURL()}.
\end{methoddesc}
+
+\subsection{NamedParamBinding}
+If an attribute is fetched other than one of those described
+in \class{_Binding}, it is taken to be the \code{opname} of a remote procedure, and a callable
+object is returned. This object dynamically parses its arguments, receives the
+reply, and parses that.
+
+\begin{classdesc}{NamedParamBinding}{\optional{**keywords}}
+For other keyword arguments see \class{_Binding}.
+\begin{tableiii}{l|c|p{20em}}{textrm}{Keyword}{Default}{Description}
+\lineiii{\code{typesmodule}}{\code{None}}{See explanation in Dispatching}
+\end{tableiii}
+\end{classdesc}
+
+\begin{methoddesc}{opname}{**kwargs}
+Using this shortcut requires that the \var{url} attribute is set, either
+throught the constructor or \method{SetURL()}.
+\end{methoddesc}
+
+
+
Modified: trunk/zsi/test/test_t4.py
===================================================================
--- trunk/zsi/test/test_t4.py 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/test/test_t4.py 2006-10-17 22:53:19 UTC (rev 1268)
@@ -27,23 +27,23 @@
r = resolvers.NetworkResolver(['http:'])
ps = ParsedSoap(IN, resolver=r.Resolve)
except ParseException, e:
- FaultFromZSIException(e).AsSOAP(OUT)
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
self.fail()
except Exception, e:
# Faulted while processing; assume it's in the header.
- FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP(OUT)
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
self.fail()
print 'resolving'
typecode = TC.Struct(None, [ TC.XML('xmltest'),
TC.String('stringtest', resolver=r.Opaque), ])
try:
dict = ps.Parse(typecode)
- #except EvaluateException, e:
- # FaultFromZSIException(e).AsSOAP(OUT)
- # sys.exit(1)
+ except EvaluateException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
except Exception, e:
# Faulted while processing; now it's the body
- FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP(OUT)
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
self.fail()
PrettyPrint(dict['xmltest'])
print '**', dict['stringtest'], '**'
Modified: trunk/zsi/test/test_t6.py
===================================================================
--- trunk/zsi/test/test_t6.py 2006-10-17 19:32:22 UTC (rev 1267)
+++ trunk/zsi/test/test_t6.py 2006-10-17 22:53:19 UTC (rev 1268)
@@ -19,19 +19,19 @@
cid = resolvers.MIMEResolver(m['content-type'], istr)
xml = cid.GetSOAPPart()
ps = ParsedSoap(xml, resolver=cid.Resolve)
- #except ParseException, e:
- # FaultFromZSIException(e).AsSOAP(OUT)
- # self.fail()
+ except ParseException, e:
+ print >>OUT, FaultFromZSIException(e).AsSOAP()
+ self.fail()
except Exception, e:
# Faulted while processing; assume it's in the header.
- FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP(OUT)
+ print >>OUT, FaultFromException(e, 1, sys.exc_info()[2]).AsSOAP()
self.fail()
try:
dict = ps.Parse(typecode)
except Exception, e:
# Faulted while processing; now it's the body
- FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP(OUT)
+ print >>OUT, FaultFromException(e, 0, sys.exc_info()[2]).AsSOAP()
self.fail()
self.failUnlessEqual(dict['stringtest'], strExtTest,
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|