Skip to content

Commit

Permalink
initial implementation for multiple method support
Browse files Browse the repository at this point in the history
  • Loading branch information
plq committed Dec 10, 2011
1 parent 9d9c2b5 commit 57a7a72
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 124 deletions.
78 changes: 39 additions & 39 deletions src/rpclib/client/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,28 @@ def __init__(self, url, app, name, out_header):
self.url = url
self.app = app

self.ctx = MethodContext(app)
self.ctx.method_request_string = name
self.ctx.out_header = out_header
initial_ctx = MethodContext(app)
initial_ctx.method_request_string = name
initial_ctx.out_header = out_header

self.app.out_protocol.set_method_descriptor(self.ctx)
self.contexts = self.app.out_protocol.generate_method_contexts(initial_ctx)

def __call__(self, *args, **kwargs):
"""Serializes its arguments, sends them, receives and deserializes the
response."""

raise NotImplementedError()

def get_out_object(self, args, kwargs):
def get_out_object(self, ctx, args, kwargs):
"""Serializes the method arguments to output document.
:param args: Sequential arguments.
:param kwargs: Name-based arguments.
"""

assert self.ctx.out_object is None
assert ctx.out_object is None

request_raw_class = self.ctx.descriptor.in_message
request_raw_class = ctx.descriptor.in_message
request_type_info = request_raw_class._type_info
request_raw = request_raw_class()

Expand All @@ -95,61 +95,61 @@ def get_out_object(self, args, kwargs):
if k in kwargs:
setattr(request_raw, k, kwargs[k])

self.ctx.out_object = iter(request_raw)
ctx.out_object = iter(request_raw)

def get_out_string(self):
def get_out_string(self, ctx):
"""Serializes the output document to a bytestream."""

assert self.ctx.out_document is None
assert self.ctx.out_string is None
assert ctx.out_document is None
assert ctx.out_string is None

self.app.out_protocol.serialize(self.ctx, message='request')
self.app.out_protocol.serialize(ctx, message='request')

if self.ctx.service_class != None:
if self.ctx.out_error is None:
self.ctx.service_class.event_manager.fire_event(
'method_return_document', self.ctx)
if ctx.service_class != None:
if ctx.out_error is None:
ctx.service_class.event_manager.fire_event(
'method_return_document', ctx)
else:
self.ctx.service_class.event_manager.fire_event(
'method_exception_document', self.ctx)
ctx.service_class.event_manager.fire_event(
'method_exception_document', ctx)

self.app.out_protocol.create_out_string(self.ctx, string_encoding)
self.app.out_protocol.create_out_string(ctx, string_encoding)

if self.ctx.service_class != None:
if self.ctx.out_error is None:
self.ctx.service_class.event_manager.fire_event(
'method_return_string', self.ctx)
if ctx.service_class != None:
if ctx.out_error is None:
ctx.service_class.event_manager.fire_event(
'method_return_string', ctx)
else:
self.ctx.service_class.event_manager.fire_event(
'method_exception_string', self.ctx)
ctx.service_class.event_manager.fire_event(
'method_exception_string', ctx)

if self.ctx.out_string is None:
self.ctx.out_string = [""]
if ctx.out_string is None:
ctx.out_string = [""]

def get_in_object(self):
def get_in_object(self, ctx):
"""Deserializes the response bytestream to input document and native
python object.
"""

assert self.ctx.in_string is not None
assert self.ctx.in_document is None
assert ctx.in_string is not None
assert ctx.in_document is None

self.app.in_protocol.create_in_document(self.ctx)
if self.ctx.service_class != None:
self.ctx.service_class.event_manager.fire_event(
'method_accept_document', self.ctx)
self.app.in_protocol.create_in_document(ctx)
if ctx.service_class != None:
ctx.service_class.event_manager.fire_event(
'method_accept_document', ctx)

# sets the ctx.in_body_doc and ctx.in_header_doc properties
self.app.in_protocol.decompose_incoming_envelope(self.ctx)
self.app.in_protocol.decompose_incoming_envelope(ctx)

# this sets ctx.in_object
self.app.in_protocol.deserialize(self.ctx, message='response')
self.app.in_protocol.deserialize(ctx, message='response')

type_info = self.ctx.descriptor.out_message._type_info
type_info = ctx.descriptor.out_message._type_info

if len(self.ctx.descriptor.out_message._type_info) == 1: # TODO: Non-Wrapped Object Support
if len(ctx.descriptor.out_message._type_info) == 1: # TODO: Non-Wrapped Object Support
wrapper_attribute = type_info.keys()[0]
self.ctx.in_object = getattr(self.ctx.in_object,
ctx.in_object = getattr(ctx.in_object,
wrapper_attribute, None)


Expand Down
20 changes: 11 additions & 9 deletions src/rpclib/client/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,30 @@

class _RemoteProcedure(RemoteProcedureBase):
def __call__(self, *args, **kwargs):
self.get_out_object(args, kwargs) # sets self.ctx.out_object
self.get_out_string() # sets self.ctx.out_string
ctx = self.contexts[0]

self.get_out_object(ctx, args, kwargs) # sets self.ctx.out_object
self.get_out_string(ctx) # sets self.ctx.out_string

out_string = ''.join(self.ctx.out_string) # FIXME: just send the iterable to the http stream.
request = Request(self.url, out_string)
code = 200
try:
response = urlopen(request)
self.ctx.in_string = [response.read()]
ctx.in_string = [response.read()]

except HTTPError, e:
code = e.code
self.ctx.in_string = [e.read()]
ctx.in_string = [e.read()]

self.get_in_object()
self.get_in_object(ctx)

if not (self.ctx.in_error is None):
raise self.ctx.in_error
if not (ctx.in_error is None):
raise ctx.in_error
elif code >= 500:
raise self.ctx.in_object
raise ctx.in_object
else:
return self.ctx.in_object
return ctx.in_object

class HttpClient(ClientBase):
def __init__(self, url, app):
Expand Down
29 changes: 20 additions & 9 deletions src/rpclib/protocol/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
logger = logging.getLogger(__name__)

import rpclib.const.xml_ns
from copy import copy

_ns_xsi = rpclib.const.xml_ns.xsi
_ns_xsd = rpclib.const.xml_ns.xsd
Expand Down Expand Up @@ -84,16 +85,17 @@ def set_app(self, value):
self.__app = value

def create_in_document(self, ctx, in_string_encoding=None):
"""Uses ctx.in_string to set ctx.in_document"""
"""Uses ``ctx.in_string`` to set ``ctx.in_document``."""

def decompose_incoming_envelope(self, ctx):
"""Sets the ctx.in_body_doc, ctx.in_header_doc and ctx.service
properties of the ctx object, if applicable.
"""Sets the ``ctx.method_request_string``, ``ctx.in_body_doc``,
``ctx.in_header_doc`` and ``ctx.service`` properties of the ctx object,
if applicable.
"""

def deserialize(self, ctx):
"""Takes a MethodContext instance and a string containing ONE document
instance in the ctx.in_string attribute.
instance in the ``ctx.in_string`` attribute.
Returns the corresponding native python object in the ctx.in_object
attribute.
Expand All @@ -115,7 +117,7 @@ def validate_document(self, payload):
validation on the parsed input document.
"""

def set_method_descriptor(self, ctx):
def generate_method_contexts(self, ctx):
"""Method to be overriden to perform any sort of custom matching between
the method_request_string and the methods.
"""
Expand All @@ -125,13 +127,22 @@ def set_method_descriptor(self, ctx):
if not name.startswith("{"):
name = '{%s}%s' % (self.app.interface.get_tns(), name)

(ctx.service_class, ctx.descriptor), = \
self.app.interface.service_method_map.get(name, (None,None))

if ctx.descriptor is None:
call_handles = self.app.interface.service_method_map.get(name, [])
if len(call_handles) == 0:
# logger.debug(pformat(ctx.app.interface.method_map.keys()))
raise ResourceNotFoundError('Method %r not found.' % name)

retval = []
for sc, d in call_handles:
c = copy(ctx)

c.descriptor = d
c.service_class = sc

retval.append(c)

return retval

def fault_to_http_response_code(self, fault):
if isinstance(fault, RequestTooLongError):
return HTTP_413
Expand Down
2 changes: 0 additions & 2 deletions src/rpclib/protocol/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ def create_in_document(self, ctx, in_string_encoding=None):
def decompose_incoming_envelope(self, ctx):
ctx.method_request_string = '{%s}%s' % (self.app.interface.get_tns(),
ctx.in_document['PATH_INFO'].split('/')[-1])

logger.debug("\033[92mMethod name: %r\033[0m" % ctx.method_request_string)

self.app.in_protocol.set_method_descriptor(ctx)
ctx.in_header_doc = _get_http_headers(ctx.in_document)
ctx.in_body_doc = parse_qs(ctx.in_document['QUERY_STRING'])

Expand Down
14 changes: 5 additions & 9 deletions src/rpclib/protocol/xml/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,30 +123,26 @@ def to_parent_element(self, cls, value, tns, parent_elt, * args, ** kwargs):
handler(self, cls, value, tns, parent_elt, * args, ** kwargs)

def validate_body(self, ctx, body_document):
"""Sets ctx.method_request_string and calls :func:`set_method_descriptor`
"""
"""Sets ctx.method_request_string and calls :func:`generate_contexts`
for validation."""
ctx.method_request_string = body_document.tag

try:
self.validate_document(body_document)
ctx.method_request_string = body_document.tag
logger.debug("%sMethod request string: %r%s" %
(LIGHT_GREEN, ctx.method_request_string, END_COLOR))
finally:
if self.log_messages:
logger.debug(etree.tostring(ctx.in_document, pretty_print=True))

if ctx.service_class is None: # i.e. if it's a server
self.set_method_descriptor(ctx)

def create_in_document(self, ctx, charset=None):
"""Uses the iterable of string fragments in ``ctx.in_string`` to set
``ctx.in_document``"""
``ctx.in_document``."""

ctx.in_document = etree.fromstring(ctx.in_string, charset)

self.validate_body(ctx, ctx.in_document)

def decompose_incoming_envelope(self, ctx):
self.validate_body(ctx, ctx.in_document)
ctx.in_header_doc = None # If you need header support, you should use Soap
ctx.in_body_doc = ctx.in_document

Expand Down
23 changes: 20 additions & 3 deletions src/rpclib/server/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ def __init__(self, app):
self.app.transport = self.transport
self.event_manager = EventManager(self)

def get_in_object(self, ctx, in_string_charset=None):
"""Uses the ctx.in_string to set ctx.in_body_doc, which in turn is used
to set ctx.in_object."""
def generate_contexts(self, ctx, in_string_charset):
"""Calls create_in_document and decompose_incoming_envelope to get
method_request string in order to generate contexts.
"""

try:
# sets ctx.in_document
Expand All @@ -55,6 +56,22 @@ def get_in_object(self, ctx, in_string_charset=None):
# sets ctx.in_body_doc and ctx.in_header_doc
self.app.in_protocol.decompose_incoming_envelope(ctx)

retval = self.app.in_protocol.generate_method_contexts(ctx)

except Fault, e:
ctx.in_object = None
ctx.in_error = e
ctx.out_error = e

retval = [ctx]

return retval

def get_in_object(self, ctx):
"""Uses the ctx.in_string to set ctx.in_body_doc, which in turn is used
to set ctx.in_object."""

try:
# sets ctx.in_object and ctx.in_header
self.app.in_protocol.deserialize(ctx, message='request')

Expand Down
Loading

0 comments on commit 57a7a72

Please sign in to comment.