Skip to content

Commit

Permalink
created MethodContext object that should be instantiated before calli…
Browse files Browse the repository at this point in the history
…ng soap message processing methods
  • Loading branch information
Burak Arslan committed Oct 9, 2010
1 parent 15c93ff commit 6e41a83
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 66 deletions.
4 changes: 3 additions & 1 deletion src/soaplib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@

const_prefmap = dict([(b,a) for a,b in const_nsmap.items()])

from _base import *
from _base import Application
from _base import ValidatingApplication
from _base import MethodContext
109 changes: 59 additions & 50 deletions src/soaplib/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import soaplib

from soaplib.serializers.exception import Fault
from soaplib.serializers.primitive import string_encoding
from soaplib.util.odict import odict

from soaplib.soap import from_soap
Expand Down Expand Up @@ -129,6 +128,19 @@ def add_complex_type(self, cls, node):
schema_info = self.get_schema_info(cls.get_namespace_prefix(self.app))
schema_info.types[cls.get_type_name()] = node

class MethodContext(object):
def __init__(self):
self.service = None
self.service_class = None

self.in_header_xml = None
self.in_body_xml = None
self.out_body = None
self.out_header_xml = None
self.out_body_xml = None

self.method_name = None

class Application(object):
transport = None

Expand All @@ -154,19 +166,17 @@ def __init__(self, services, tns, name=None, _with_partnerlink=False):

self.build_schema()

def __decompose_request(self, envelope_string, charset=None):
service_ctx = None
method_name = None

# deserialize the body of the message
def __decompose_request(self, ctx, envelope_string, charset):
header, body = from_soap(envelope_string, charset)

print header,body

if not (body is None):
try:
self.validate_request(body)
if not (body is None):
method_name = body.tag
logger.debug("\033[92mMethod name: %r\033[0m" % method_name)
ctx.method_name = body.tag
logger.debug("\033[92mMethod name: %r\033[0m" % ctx.method_name)

finally:
# for performance reasons, we don't want the following to run
Expand All @@ -180,19 +190,13 @@ def __decompose_request(self, envelope_string, charset=None):
raise Fault('Client.XMLSyntax', 'Error at line: %d, '
'col: %d' % e.position)

if method_name is None:
raise Exception("Could not extract method name from the request!")

service_class = self.get_service_class(method_name)
service_ctx = self.get_service(service_class)

service_ctx.header_xml = header
service_ctx.body_xml = body
service_ctx.method_name = method_name
ctx.service_class = self.get_service_class(ctx.method_name)
ctx.service = self.get_service(ctx.service_class)

return service_ctx
ctx.in_header_xml = header
ctx.in_body_xml = body

def deserialize_soap(self, envelope_string, charset=None):
def deserialize_soap(self, ctx, envelope_string, charset=None):
"""Takes a string containing ONE soap message.
Returns the corresponding native python object, along with the request
context
Expand All @@ -201,27 +205,30 @@ def deserialize_soap(self, envelope_string, charset=None):
"""

try:
ctx = self.__decompose_request(envelope_string, charset)
self.__decompose_request(ctx, envelope_string, charset)
except ValidationError, e:
return None, e
return e

# retrieve the method descriptor
descriptor = ctx.descriptor = ctx.get_method(ctx.method_name)
if ctx.method_name is None:
raise Exception("Could not extract method name from the request!")
else:
descriptor = ctx.descriptor = ctx.service.get_method(ctx.method_name)

# decode header object
if ctx.header_xml is not None and len(ctx.header_xml) > 0:
if ctx.in_header_xml is not None and len(ctx.in_header_xml) > 0:
in_header = descriptor.in_header
ctx.soap_in_header = in_header.from_xml(ctx.header_xml)
ctx.service.in_header = in_header.from_xml(ctx.in_header_xml)

# decode method arguments
if ctx.body_xml is not None and len(ctx.body_xml) > 0:
params = descriptor.in_message.from_xml(ctx.body_xml)
if ctx.in_body_xml is not None and len(ctx.in_body_xml) > 0:
params = descriptor.in_message.from_xml(ctx.in_body_xml)
else:
params = [None] * len(descriptor.in_message._type_info)

return ctx, params
return params

def process_request(self,ctx,req_obj):
def process_request(self, ctx, req_obj):
"""Takes the native request object.
Returns the response to the request as a native python object.
Expand All @@ -230,15 +237,20 @@ def process_request(self,ctx,req_obj):

try:
# retrieve the method
func = getattr(ctx, ctx.descriptor.name)
func = getattr(ctx.service, ctx.descriptor.name)

# call the method
return ctx.call_wrapper(func, req_obj)
return ctx.service.call_wrapper(func, req_obj)

except Fault, e:
stacktrace=traceback.format_exc()
logger.error(stacktrace)
return e

except Exception, e:
stacktrace=traceback.format_exc()
logger.error(stacktrace)

fault = Fault('Server', str(e))

return fault
Expand All @@ -254,53 +266,51 @@ def serialize_soap(self, ctx, native_obj):
envelope = etree.Element('{%s}Envelope' % soaplib.ns_soap_env,
nsmap=self.nsmap)

if ctx is None or isinstance(native_obj, Exception):
if isinstance(native_obj, Exception):
stacktrace=traceback.format_exc()
logger.error(stacktrace)

# implementation hook
if not (ctx is None):
ctx.on_method_exception_object(native_obj)
ctx.service.on_method_exception_object(native_obj)
self.on_exception_object(native_obj)

# FIXME: There's no way to alter soap response headers for the user.
soap_body = etree.SubElement(envelope,
out_body_xml = etree.SubElement(envelope,
'{%s}Body' % soaplib.ns_soap_env, nsmap=self.nsmap)
native_obj.__class__.to_xml(native_obj, self.get_tns(), soap_body)
native_obj.__class__.to_xml(native_obj, self.get_tns(), out_body_xml)

# implementation hook
if not (ctx is None):
ctx.on_method_exception_xml(soap_body)
ctx.soap_body = soap_body
self.on_exception_xml(soap_body)
if not (ctx.service is None):
ctx.service.on_method_exception_xml(out_body_xml)
ctx.out_body_xml = out_body_xml
self.on_exception_xml(out_body_xml)

if logger.level == logging.DEBUG:
logger.debug(etree.tostring(envelope, pretty_print=True))

else:
#
# header
#
if ctx.soap_out_header != None:
if ctx.service.out_header != None:
if ctx.descriptor.out_header is None:
logger.warning(
"Skipping soap response header as %r method is not "
"published to have a soap response header" %
native_obj.get_type_name()[:-len('Response')])

else:
soap_header_elt = etree.SubElement(envelope,
'{%s}Header' % soaplib.ns_soap_env)
ctx.out_header_xml = soap_header_elt = etree.SubElement(
envelope, '{%s}Header' % soaplib.ns_soap_env)

ctx.descriptor.out_header.to_xml(
ctx.soap_out_header,
ctx.service.out_header,
self.get_tns(),
soap_header_elt,
ctx.descriptor.out_header.get_type_name()
)

#
# body
#
ctx.soap_body = soap_body = etree.SubElement(envelope,
ctx.out_body_xml = out_body_xml = etree.SubElement(envelope,
'{%s}Body' % soaplib.ns_soap_env)

# instantiate the result message
Expand All @@ -320,15 +330,14 @@ def serialize_soap(self, ctx, native_obj):

# transform the results into an element
ctx.descriptor.out_message.to_xml(
result_message, self.get_tns(), soap_body)
result_message, self.get_tns(), out_body_xml)

if logger.level == logging.DEBUG:
logger.debug('\033[91m'+ "Response" + '\033[0m')
logger.debug(etree.tostring(envelope, xml_declaration=True,
pretty_print=True))

return etree.tostring(envelope, xml_declaration=True,
encoding=string_encoding)
return envelope

def get_namespace_prefix(self, ns):
"""Returns the namespace prefix for the given namespace. Creates a new
Expand Down
4 changes: 2 additions & 2 deletions src/soaplib/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ class will use the rpc decorator to flag methods to be exposed via soap.
__out_header__ = None

def __init__(self, environ=None):
self.soap_in_header = None
self.soap_out_header = None
self.in_header = None
self.out_header = None

cls = self.__class__
if not (cls in _public_methods_cache):
Expand Down
32 changes: 19 additions & 13 deletions src/soaplib/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#

from lxml import etree

import logging
logger = logging.getLogger(__name__)

Expand All @@ -26,6 +28,7 @@
import soaplib

from soaplib.serializers.exception import Fault
from soaplib.serializers.primitive import string_encoding

from soaplib.mime import apply_mtom
from soaplib.mime import collapse_swa
Expand Down Expand Up @@ -135,40 +138,43 @@ def __handle_soap_request(self, req_env, start_response):

http_payload, charset = _reconstruct_soap_request(req_env)

service, params = self.deserialize_soap(http_payload, charset)
ctx = soaplib.MethodContext()
params = self.deserialize_soap(ctx, http_payload, charset)

if service is None:
result_str = self.serialize_soap(service, params)
if ctx.service is None:
envelope_str = self.serialize_soap(ctx, params)
return_code = HTTP_500

else:
# implementation hook
service.on_method_call(req_env,method_name,params,service.body_xml)
ctx.service.on_method_call(req_env,method_name,params,ctx.in_body_xml)

result_raw = self.process_request(service, params)
result_raw = self.process_request(ctx, params)

if isinstance(result_raw, Exception):
return_code = HTTP_500

# implementation hook
service.on_method_return(req_env, result_raw, service.body_xml,
ctx.service.on_method_return(req_env, result_raw, ctx.in_body_xml,
http_resp_headers)

result_str = self.serialize_soap(service, result_raw)
envelope_xml = self.serialize_soap(ctx, result_raw)
envelope_str = etree.tostring(envelope_xml, xml_declaration=True,
encoding=string_encoding)

# implementation hook
self.on_return(req_env, http_resp_headers, result_str)
self.on_return(req_env, http_resp_headers, envelope_str)

if service.descriptor.mtom:
http_resp_headers, result_str = apply_mtom(http_resp_headers,
result_str, service.descriptor.out_message._type_info,
if ctx.descriptor.mtom:
http_resp_headers, envelope_str = apply_mtom(http_resp_headers,
envelope_str, ctx.descriptor.out_message._type_info,
[result_raw])

# initiate the response
http_resp_headers['Content-Length'] = str(len(result_str))
http_resp_headers['Content-Length'] = str(len(envelope_str))
start_response(return_code, http_resp_headers.items())

return [result_str]
return [envelope_str]

def on_call(self, environ):
'''This is the first method called when this WSGI app is invoked
Expand Down

0 comments on commit 6e41a83

Please sign in to comment.