You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/contrib/spyne/client/_base.py

177 lines
6.0 KiB

#
# spyne - Copyright (C) Spyne contributors.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#
"""Contains the ClientBase class and its helper objects."""
from spyne.context import MethodContext
from spyne.model.primitive import string_encoding
class Factory(object):
def __init__(self, app):
self.__app = app
def create(self, object_name):
return self.__app.interface.get_class_instance(object_name)
class RemoteService(object):
def __init__(self, rpc_class, url, app, *args, **kwargs):
self.__app = app
self.__url = url
self.out_header = None
self.rpc_class = rpc_class
self.args = args
self.kwargs = kwargs
def __getattr__(self, key):
return self.rpc_class(self.__url, self.__app, key, self.out_header,
*self.args, **self.kwargs)
class RemoteProcedureBase(object):
"""Abstract base class that handles all (de)serialization.
Child classes must implement the client transport in the __call__ method
using the following method signature: ::
def __call__(self, *args, **kwargs):
:param url: The url for the server endpoint.
:param app: The application instance the client belongs to.
:param name: The string identifier for the remote method.
:param out_header: The header that's going to be sent with the remote call.
"""
def __init__(self, url, app, name, out_header=None):
self.url = url
self.app = app
initial_ctx = MethodContext(self, MethodContext.CLIENT)
initial_ctx.method_request_string = name
initial_ctx.out_header = out_header
self.contexts = initial_ctx.out_protocol.generate_method_contexts(
initial_ctx)
def __call__(self, *args, **kwargs):
"""Serializes its arguments, sends them, receives and deserializes the
response and returns it."""
raise NotImplementedError()
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 ctx.out_object is None
request_raw_class = ctx.descriptor.in_message
request_type_info = request_raw_class._type_info
request_raw = request_raw_class()
for i in range(len(request_type_info)):
if i < len(args):
setattr(request_raw, request_type_info.keys()[i], args[i])
else:
setattr(request_raw, request_type_info.keys()[i], None)
for k in request_type_info:
if k in kwargs:
setattr(request_raw, k, kwargs[k])
ctx.out_object = list(request_raw)
def get_out_string(self, ctx):
"""Serializes the output document to a bytestream."""
assert ctx.out_document is None
assert ctx.out_string is None
ctx.out_protocol.serialize(ctx, ctx.out_protocol.REQUEST)
if ctx.out_error is None:
ctx.fire_event('method_return_document')
else:
ctx.fire_event('method_exception_document')
ctx.out_protocol.create_out_string(ctx, string_encoding)
if ctx.out_error is None:
ctx.fire_event('method_return_string')
else:
ctx.fire_event('method_exception_string')
if ctx.out_string is None:
ctx.out_string = [""]
def get_in_object(self, ctx):
"""Deserializes the response bytestream first as a document and then
as a native python object.
"""
assert ctx.in_string is not None
assert ctx.in_document is None
self.app.in_protocol.create_in_document(ctx)
ctx.fire_event('method_accept_document')
# sets the ctx.in_body_doc and ctx.in_header_doc properties
self.app.in_protocol.decompose_incoming_envelope(ctx,
message=self.app.in_protocol.RESPONSE)
# this sets ctx.in_object
self.app.in_protocol.deserialize(ctx,
message=self.app.in_protocol.RESPONSE)
type_info = ctx.descriptor.out_message._type_info
# TODO: Non-Wrapped Object Support
if len(ctx.descriptor.out_message._type_info) == 1:
wrapper_attribute = type_info.keys()[0]
ctx.in_object = getattr(ctx.in_object, wrapper_attribute, None)
class ClientBase(object):
"""The base class for all client applications. ``self.service`` attribute
should be initialized in the constructor of the child class.
"""
def __init__(self, url, app):
self.factory = Factory(app)
def set_options(self, **kwargs):
"""Sets call options.
:param out_header: Sets the header object that's going to be sent with
the remote procedure call.
:param soapheaders: A suds-compatible alias for out_header.
"""
if ('soapheaders' in kwargs) and ('out_header' in kwargs):
raise ValueError('you should specify only one of "soapheaders" or '
'"out_header" keyword arguments.')
self.service.out_header = kwargs.get('soapheaders', None)
if self.service.out_header is None:
self.service.out_header = kwargs.get('out_header', None)