def test_add_to_schema_w_extends(self): from spyne.const.xml import XSD class base(Fault): __namespace__ = 'ns' @classmethod def get_type_name_ns(self, app): return 'testing:Base' class cls(Fault): __namespace__ = 'ns' @classmethod def get_type_name_ns(self, app): return 'testing:My' interface = Interface(FakeApp()) interface.add_class(cls) pref = cls.get_namespace_prefix(interface) wsdl = Wsdl11(interface) wsdl.build_interface_document('prot://addr') schema = wsdl.get_schema_info(pref) self.assertEqual(len(schema.types), 1) self.assertEqual(len(interface.classes), 1) c_cls = next(iter(interface.classes.values())) c_elt = next(iter(schema.types.values())) self.failUnless(c_cls is cls) self.assertEqual(c_elt.tag, XSD('complexType')) self.assertEqual(c_elt.get('name'), 'cls') from lxml import etree print(etree.tostring(c_elt, pretty_print=True)) self.assertEqual(len(c_elt), 0)
def __init__(self, services, tns, name=None, prefix_namespace=None, names_parts_in_messages=None, in_protocol=None, out_protocol=None, config=None, classes=()): self.services = tuple(services) self.tns = tns self.name = name self.config = config self.classes = classes self.names_parts_in_messages = names_parts_in_messages if prefix_namespace is None: self.prefix_namespace = DEFAULT_PREFIX_NAMESPACE else: self.prefix_namespace = prefix_namespace if self.name is None: self.name = self.__class__.__name__.split('.')[-1] logger.info("Initializing application {%s}%s...", self.tns, self.name) self.event_manager = EventManager(self) self.error_handler = None self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.check_unique_method_keys() # is this really necessary nowadays? # this needs to be after protocol assignments to give _static_when # functions as much info as possible about the application self.interface = Interface(self) # set_app needs to be after interface init because the protocols use it. self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self)
def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, interface=None): self.services = tuple(services) self.tns = tns self.name = name if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self) self.reinitialize()
def test_add_to_schema_no_extends(self): from spyne.const.xml import XSD class cls(Fault): __namespace__='ns' @classmethod def get_type_name_ns(self, app): return 'testing:My' interface = Interface(FakeApp()) interface.add_class(cls) pref = cls.get_namespace_prefix(interface) wsdl = Wsdl11(interface) wsdl.build_interface_document('prot://addr') schema = wsdl.get_schema_info(pref) self.assertEqual(len(schema.types), 1) c_cls = interface.classes['{ns}cls'] c_elt = schema.types[0] self.assertTrue(c_cls is cls) self.assertEqual(c_elt.tag, XSD('complexType')) self.assertEqual(c_elt.get('name'), 'cls') self.assertEqual(len(schema.elements), 1) e_elt = schema.elements.values()[0] self.assertEqual(e_elt.tag, XSD('element')) self.assertEqual(e_elt.get('name'), 'cls') self.assertEqual(e_elt.get('type'), 'testing:My') self.assertEqual(len(e_elt), 0)
def test_add_to_schema_w_extends(self): import spyne.const.xml_ns ns_xsd = spyne.const.xml_ns.xsd class base(Fault): __namespace__ = 'ns' @classmethod def get_type_name_ns(self, app): return 'testing:Base' class cls(Fault): __namespace__ = 'ns' @classmethod def get_type_name_ns(self, app): return 'testing:My' interface = Interface(FakeApp()) interface.add_class(cls) pref = cls.get_namespace_prefix(interface) wsdl = Wsdl11(interface) wsdl.build_interface_document('prot://addr') schema = wsdl.get_schema_info(pref) self.assertEqual(len(schema.types), 1) self.assertEqual(len(interface.classes), 1) c_cls = interface.classes.values()[0] c_elt = schema.types.values()[0] print(c_cls, cls) self.failUnless(c_cls is cls) self.assertEqual(c_elt.tag, '{%s}complexType' % ns_xsd) self.assertEqual(c_elt.get('name'), 'Fault') self.assertEqual(len(c_elt), 0)
def get_schema_documents(models, default_namespace=None): '''Returns the schema documents in a dict whose keys are namespace prefixes and values are Element objects. :param models: A list of spyne.model classes that will be represented in the schema. ''' if default_namespace is None: default_namespace = models[0].get_namespace() fake_app = FakeApplication() fake_app.tns = default_namespace fake_app.services = [] interface = Interface(fake_app) for m in models: interface.add_class(m) interface.populate_interface(fake_app) document = XmlSchema(interface) document.build_interface_document() return document.get_interface_document()
def test_add_to_schema_no_extends(self): import spyne.const.xml_ns ns_xsd = spyne.const.xml_ns.xsd class cls(Fault): __namespace__='ns' @classmethod def get_type_name_ns(self, app): return 'testing:My' interface = Interface(FakeApp()) interface.add_class(cls) pref = cls.get_namespace_prefix(interface) wsdl = Wsdl11(interface) wsdl.build_interface_document('prot://addr') schema = wsdl.get_schema_info(pref) self.assertEqual(len(schema.types), 1) c_cls = interface.classes['{ns}cls'] c_elt = schema.types[0] self.failUnless(c_cls is cls) self.assertEqual(c_elt.tag, '{%s}complexType' % ns_xsd) self.assertEqual(c_elt.get('name'), 'cls') self.assertEqual(len(schema.elements), 1) e_elt = schema.elements.values()[0] self.assertEqual(e_elt.tag, '{%s}element' % ns_xsd) self.assertEqual(e_elt.get('name'), 'cls') self.assertEqual(e_elt.get('type'), 'testing:My') self.assertEqual(len(e_elt), 0)
def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, config=None): self.services = tuple(services) self.tns = tns self.name = name self.config = config if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self)
def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, interface=None): self.services = tuple(services) self.tns = tns self.name = name if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase(self) else: self.in_protocol.set_app(self) if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase(self) else: self.out_protocol.set_app(self) register_application(self) self.reinitialize()
def get_validation_schema(models, default_namespace=None): """Returns the validation schema object for the given models. :param models: A list of spyne.model classes that will be represented in the schema. """ if default_namespace is None: default_namespace = models[0].get_namespace() fake_app = FakeApplication(default_namespace) interface = Interface(fake_app) for m in models: m.resolve_namespace(m, default_namespace) interface.add_class(m) schema = XmlSchema(interface) schema.build_validation_schema() return schema.validation_schema
def get_schema_documents(models, default_namespace=None): """Returns the schema documents in a dict whose keys are namespace prefixes and values are Element objects. :param models: A list of spyne.model classes that will be represented in the schema. """ if default_namespace is None: default_namespace = models[0].get_namespace() fake_app = FakeApplication() fake_app.tns = default_namespace fake_app.services = [] interface = Interface(fake_app) for m in models: m.resolve_namespace(m, default_namespace) interface.add_class(m) interface.populate_interface(fake_app) document = XmlSchema(interface) document.build_interface_document() return document.get_interface_document()
def get_validation_schema(models, default_namespace=None): '''Returns the validation schema object for the given models. :param models: A list of spyne.model classes that will be represented in the schema. ''' if default_namespace is None: default_namespace = models[0].get_namespace() fake_app = FakeApplication() fake_app.tns = default_namespace fake_app.services = [] interface = Interface(fake_app) for m in models: interface.add_class(m) schema = XmlSchema(interface) schema.build_validation_schema() return schema.validation_schema
def test_add_to_schema(self): class CM(ComplexModel): i = Integer s = String a = XmlAttribute(String) app = FakeApp() app.tns = 'tns' CM.resolve_namespace(CM, app.tns) interface = Interface(app) interface.add_class(CM) wsdl = Wsdl11(interface) wsdl.build_interface_document('http://a-aaaa.com') pref = CM.get_namespace_prefix(interface) type_def = wsdl.get_schema_info(pref).types[CM.get_type_name()] attribute_def = type_def.find('{%s}attribute' % xml_ns.xsd) print(etree.tostring(type_def, pretty_print=True)) self.assertIsNotNone(attribute_def) self.assertEqual(attribute_def.get('name'), 'a') self.assertEqual(attribute_def.get('type'), CM.a.type.get_type_name_ns(interface))
class Application(object): """The Application class is the glue between one or more service definitions, input and output protocols. :param services: An iterable of ServiceBase subclasses that defines the exposed services. :param tns: The targetNamespace attribute of the exposed service. :param name: The optional name attribute of the exposed service. The default is the name of the application class which is by default 'Application'. :param in_protocol: A ProtocolBase instance that denotes the input protocol. It's only optional for NullServer transport. :param out_protocol: A ProtocolBase instance that denotes the output protocol. It's only optional for NullServer transport. :param config: An arbitrary python object to store random global data. Supported events: * ``method_call``: Called right before the service method is executed * ``method_return_object``: Called right after the service method is executed * ``method_exception_object``: Called when an exception occurred in a service method, before the exception is serialized. * ``method_context_created``: Called from the constructor of the MethodContext instance. * ``method_context_closed``: Called from the ``close()`` function of the MethodContext instance, which in turn is called by the transport when the response is fully sent to the client (or in the client case, the response is fully received from server). """ transport = None def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, config=None): self.services = tuple(services) self.tns = tns self.name = name self.config = config if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self) def process_request(self, ctx): """Takes a MethodContext instance. Returns the response to the request as a native python object. If the function throws an exception, it returns None and sets the exception object to ctx.out_error. Overriding this method would break event management. So this is not meant to be overridden unless you know what you're doing. """ try: # fire events self.event_manager.fire_event('method_call', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event('method_call', ctx) # in object is always a sequence of incoming values. We need to fix # that for bare mode. if ctx.descriptor.body_style is BODY_STYLE_BARE: ctx.in_object = [ctx.in_object] elif ctx.descriptor.body_style is BODY_STYLE_EMPTY: ctx.in_object = [] # call user method ctx.out_object = self.call_wrapper(ctx) # out object is always a sequence of return values. see # MethodContext docstrings for more info if ctx.descriptor.body_style is not BODY_STYLE_WRAPPED or \ len(ctx.descriptor.out_message._type_info) <= 1: # if it's not a wrapped method, OR there's just one return type # we wrap it ourselves ctx.out_object = [ctx.out_object] # Now that the processing is switched to the outgoing message, # point ctx.protocol to ctx.out_protocol ctx.protocol = ctx.outprot_ctx # fire events self.event_manager.fire_event('method_return_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_return_object', ctx) except Redirect as e: try: e.do_redirect() ctx.out_object = [None] # Now that the processing is switched to the outgoing message, # point ctx.protocol to ctx.out_protocol ctx.protocol = ctx.outprot_ctx # fire events self.event_manager.fire_event('method_redirect', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_redirect', ctx) except Exception as e: logger_server.exception(e) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) # fire events self.event_manager.fire_event('method_redirect_exception', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_redirect_exception', ctx) except Fault as e: if e.faultcode == 'Client' or e.faultcode.startswith('Client.'): logger_client.exception(e) else: logger.exception(e) ctx.out_error = e # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) # we don't catch BaseException because we actually don't want to catch # "system-exiting" exceptions. See: # https://docs.python.org/2/library/exceptions.html#exceptions.Exception except Exception as e: logger_server.critical(e, **{'exc_info': 1}) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) def call_wrapper(self, ctx): """This method calls the call_wrapper method in the service definition. This can be overridden to make an application-wide custom exception management. """ retval = None # service rpc if ctx.descriptor.no_self: retval = ctx.descriptor.service_class.call_wrapper(ctx) # class rpc else: cls = ctx.descriptor.parent_class if cls.__orig__ is not None: cls = cls.__orig__ inst = cls.__respawn__(ctx) if inst is None: raise RespawnError('{%s}%s' % (cls.get_namespace(), cls.get_type_name())) in_cls = ctx.descriptor.in_message args = ctx.in_object if args is None: args = [] elif ctx.descriptor.body_style is BODY_STYLE_WRAPPED and \ len(in_cls.get_flat_type_info(in_cls)) <= 1: args = [] else: args = args[1:] if ctx.descriptor.service_class is not None: ctx.in_object = [inst, ctx] ctx.in_object.extend(args) # hack to make sure inst goes first ctx.descriptor.no_ctx = True retval = ctx.descriptor.service_class.call_wrapper(ctx) elif ctx.function is not None: if ctx.descriptor.no_ctx: retval = ctx.function(inst, *args) else: retval = ctx.function(inst, ctx, *args) return retval def _has_callbacks(self): return self.interface._has_callbacks() def reinitialize(self, server): """This is normally called on transport instantiation by ServerBase""" seen = set() from spyne import MethodDescriptor for d in self.interface.method_id_map.values(): assert isinstance(d, MethodDescriptor) if d.aux is not None and not id(d.aux) in seen: d.aux.initialize(server) seen.add(id(d.aux)) if d.service_class is not None and not id(d.service_class) in seen: d.service_class.initialize(server) seen.add(id(d.service_class)) def __hash__(self): return hash(tuple((id(s) for s in self.services)))
class Application(object): """The Application class is the glue between one or more service definitions, input and output protocols. :param services: An iterable of ServiceBase subclasses that defines the exposed services. :param tns: The targetNamespace attribute of the exposed service. :param name: The optional name attribute of the exposed service. The default is the name of the application class which is by default 'Application'. :param in_protocol: A ProtocolBase instance that denotes the input protocol. It's only optional for NullServer transport. :param out_protocol: A ProtocolBase instance that denotes the output protocol. It's only optional for NullServer transport. :param interface: Ignored. Kept for backwards-compatibility purposes. Supported events: * ``method_call``: Called right before the service method is executed * ``method_return_object``: Called right after the service method is executed * ``method_exception_object``: Called when an exception occurred in a service method, before the exception is serialized. * ``method_context_created``: Called from the constructor of the MethodContext instance. * ``method_context_closed``: Called from the ``close()`` function of the MethodContext instance, which in turn is called by the transport when the response is fully sent to the client (or in the client case, the response is fully received from server). """ transport = None def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, interface=None): self.services = tuple(services) self.tns = tns self.name = name if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self) self.reinitialize() def process_request(self, ctx): """Takes a MethodContext instance. Returns the response to the request as a native python object. If the function throws an exception, it returns None and sets the exception object to ctx.out_error. Overriding this method would break event management. So this is not meant to be overridden unless you know what you're doing. """ try: # fire events self.event_manager.fire_event('method_call', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event('method_call', ctx) # call the method ctx.out_object = self.call_wrapper(ctx) # out object is always an iterable of return values. see # MethodContext docstrings for more info if ctx.descriptor.body_style is not BODY_STYLE_WRAPPED or \ len(ctx.descriptor.out_message._type_info) <= 1: # the return value should already be wrapped by a sequence. ctx.out_object = [ctx.out_object] # fire events self.event_manager.fire_event('method_return_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_return_object', ctx) except Fault as e: if e.faultcode == 'Client' or e.faultcode.startswith('Client.'): logger_client.exception(e) else: logger.exception(e) ctx.out_error = e # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) except Exception as e: logger.exception(e) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) def call_wrapper(self, ctx): """This method calls the call_wrapper method in the service definition. This can be overridden to make an application-wide custom exception management. """ if ctx.descriptor.body_style is BODY_STYLE_BARE: ctx.in_object = [ctx.in_object] elif ctx.descriptor.body_style is BODY_STYLE_EMPTY: ctx.in_object = [] retval = None # service rpc if ctx.descriptor.no_self: retval = ctx.descriptor.service_class.call_wrapper(ctx) # class rpc else: cls = ctx.descriptor.parent_class if cls.__orig__ is not None: cls = cls.__orig__ inst = cls.__respawn__(ctx) if inst is None: raise RespawnError('{%s}%s' % (cls.get_namespace(), cls.get_type_name())) in_cls = ctx.descriptor.in_message args = ctx.in_object if args is None: args = [] elif ctx.descriptor.body_style is BODY_STYLE_WRAPPED and \ len(in_cls.get_flat_type_info(in_cls)) <= 1: args = [] else: args = args[1:] if ctx.descriptor.service_class is not None: ctx.in_object = [inst, ctx] ctx.in_object.extend(args) # hack to make sure inst goes first ctx.descriptor.no_ctx = True retval = ctx.descriptor.service_class.call_wrapper(ctx) elif ctx.function is not None: if ctx.descriptor.no_ctx: retval = ctx.function(inst, *args) else: retval = ctx.function(inst, ctx, *args) return retval def _has_callbacks(self): return self.interface._has_callbacks() def reinitialize(self): from spyne.server import ServerBase server = ServerBase(self) aux_memo = set() for d in self.interface.method_id_map.values(): if d.aux is not None and not id(d.aux) in aux_memo: d.aux.initialize(server) aux_memo.add(id(d.aux)) def __hash__(self): return hash(tuple((id(s) for s in self.services)))
class Application(object): """The Application class is the glue between one or more service definitions, input and output protocols. :param services: An iterable of ServiceBase subclasses that defines the exposed services. :param tns: The targetNamespace attribute of the exposed service. :param name: The optional name attribute of the exposed service. The default is the name of the application class which is by default 'Application'. :param in_protocol: A ProtocolBase instance that denotes the input protocol. It's only optional for NullServer transport. :param out_protocol: A ProtocolBase instance that denotes the output protocol. It's only optional for NullServer transport. :param interface: Ignored. Kept for backwards-compatibility purposes. Supported events: * ``method_call``: Called right before the service method is executed * ``method_return_object``: Called right after the service method is executed * ``method_exception_object``: Called when an exception occurred in a service method, before the exception is serialized. * ``method_context_created``: Called from the constructor of the MethodContext instance. * ``method_context_closed``: Called from the ``close()`` function of the MethodContext instance, which in turn is called by the transport when the response is fully sent to the client (or in the client case, the response is fully received from server). """ transport = None def __init__(self, services, tns, name=None, in_protocol=None, out_protocol=None, interface=None): self.services = tuple(services) self.tns = tns self.name = name if self.name is None: self.name = self.__class__.__name__.split('.')[-1] self.event_manager = EventManager(self) self.error_handler = None self.interface = Interface(self) self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self) self.reinitialize() def process_request(self, ctx): """Takes a MethodContext instance. Returns the response to the request as a native python object. If the function throws an exception, it returns None and sets the exception object to ctx.out_error. Overriding this method would break event management. So this is not meant to be overridden unless you know what you're doing. """ try: # fire events self.event_manager.fire_event('method_call', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event('method_call', ctx) # call the method ctx.out_object = self.call_wrapper(ctx) # out object is always an iterable of return values. see # MethodContext docstrings for more info if ctx.descriptor.body_style is not BODY_STYLE_WRAPPED or \ len(ctx.descriptor.out_message._type_info) <= 1: # the return value should already be wrapped by a sequence. ctx.out_object = [ctx.out_object] # fire events self.event_manager.fire_event('method_return_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_return_object', ctx) except Fault as e: if e.faultcode == 'Client' or e.faultcode.startswith('Client.'): logger_client.exception(e) else: logger.exception(e) ctx.out_error = e # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) except Exception as e: logger.exception(e) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) # fire events self.event_manager.fire_event('method_exception_object', ctx) if ctx.service_class is not None: ctx.service_class.event_manager.fire_event( 'method_exception_object', ctx) def call_wrapper(self, ctx): """This method calls the call_wrapper method in the service definition. This can be overridden to make an application-wide custom exception management. """ if ctx.descriptor.body_style is BODY_STYLE_BARE: ctx.in_object = [ctx.in_object] elif ctx.descriptor.body_style is BODY_STYLE_EMPTY: ctx.in_object = [] # service rpc if ctx.descriptor.service_class is not None: return ctx.descriptor.service_class.call_wrapper(ctx) # class rpc cls = ctx.descriptor.parent_class if cls.__orig__ is not None: cls = cls.__orig__ inst = cls.__respawn__(ctx) if inst is None: raise ResourceNotFoundError('{%s}%s' % (cls.get_namespace(), cls.get_type_name())) args = ctx.in_object[1:] if ctx.function is not None: if ctx.descriptor.no_ctx: return ctx.function(inst, *args) else: return ctx.function(inst, ctx, *args) def _has_callbacks(self): return self.interface._has_callbacks() def reinitialize(self): from spyne.server import ServerBase server = ServerBase(self) aux_memo = set() for d in self.interface.method_id_map.values(): if d.aux is not None and not id(d.aux) in aux_memo: d.aux.initialize(server) aux_memo.add(id(d.aux)) def __hash__(self): return hash(tuple((id(s) for s in self.services)))
class Application(object): """The Application class is the glue between one or more service definitions, input and output protocols. :param services: An iterable of Service subclasses that defines the exposed services. :param tns: The targetNamespace attribute of the exposed service. :param name: The optional name attribute of the exposed service. The default is the name of the application class which is by default 'Application'. :param in_protocol: A ProtocolBase instance that denotes the input protocol. It's only optional for NullServer transport. :param out_protocol: A ProtocolBase instance that denotes the output protocol. It's only optional for NullServer transport. :param config: An arbitrary python object to store random global data. Supported events: * ``method_call``: Called right before the service method is executed * ``method_return_object``: Called right after the service method is executed * ``method_exception_object``: Called when an exception occurred in a service method, before the exception is serialized. * ``method_context_created``: Called from the constructor of the MethodContext instance. * ``method_context_closed``: Called from the ``close()`` function of the MethodContext instance, which in turn is called by the transport when the response is fully sent to the client (or in the client case, the response is fully received from server). """ transport = None def __init__(self, services, tns, name=None, prefix_namespace=None, names_parts_in_messages=None, in_protocol=None, out_protocol=None, config=None, classes=()): self.services = tuple(services) self.tns = tns self.name = name self.config = config self.classes = classes self.names_parts_in_messages = names_parts_in_messages if prefix_namespace is None: self.prefix_namespace = DEFAULT_PREFIX_NAMESPACE else: self.prefix_namespace = prefix_namespace if self.name is None: self.name = self.__class__.__name__.split('.')[-1] logger.info("Initializing application {%s}%s...", self.tns, self.name) self.event_manager = EventManager(self) self.error_handler = None self.in_protocol = in_protocol self.out_protocol = out_protocol if self.in_protocol is None: from spyne.protocol import ProtocolBase self.in_protocol = ProtocolBase() if self.out_protocol is None: from spyne.protocol import ProtocolBase self.out_protocol = ProtocolBase() self.check_unique_method_keys() # is this really necessary nowadays? # this needs to be after protocol assignments to give _static_when # functions as much info as possible about the application self.interface = Interface(self) # set_app needs to be after interface init because the protocols use it. self.in_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.in_protocol.message = self.in_protocol.REQUEST self.out_protocol.set_app(self) # FIXME: this normally is another parameter to set_app but it's kept # separate for backwards compatibility reasons. self.out_protocol.message = self.out_protocol.RESPONSE register_application(self) def process_request(self, ctx): """Takes a MethodContext instance. Returns the response to the request as a native python object. If the function throws an exception, it returns None and sets the exception object to ctx.out_error. Overriding this method would break event management. So this is not meant to be overridden unless you know what you're doing. """ try: ctx.fire_event('method_call') # in object is always a sequence of incoming values. We need to fix # that for bare mode. if ctx.descriptor.body_style is BODY_STYLE_BARE: ctx.in_object = [ctx.in_object] elif ctx.descriptor.body_style is BODY_STYLE_EMPTY: ctx.in_object = [] # call user method ctx.out_object = self.call_wrapper(ctx) # out object is always a sequence of return values. see # MethodContext docstrings for more info if ctx.descriptor.body_style is not BODY_STYLE_WRAPPED or \ len(ctx.descriptor.out_message._type_info) <= 1: # if it's not a wrapped method, OR there's just one return type # we wrap it ourselves ctx.out_object = [ctx.out_object] # Now that the processing is switched to the outgoing message, # point ctx.protocol to ctx.out_protocol ctx.protocol = ctx.outprot_ctx ctx.fire_event('method_return_object') except Redirect as e: try: e.do_redirect() ctx.out_object = [None] # Now that the processing is switched to the outgoing message, # point ctx.protocol to ctx.out_protocol ctx.protocol = ctx.outprot_ctx ctx.fire_event('method_redirect') except Exception as e: logger_server.exception(e) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) ctx.fire_event('method_redirect_exception') except Fault as e: if e.faultcode == 'Client' or e.faultcode.startswith('Client.'): logger_client.exception(e) else: logger.exception(e) ctx.out_error = e ctx.fire_event('method_exception_object') # we don't catch BaseException because we actually don't want to catch # "system-exiting" exceptions. See: # https://docs.python.org/2/library/exceptions.html#exceptions.Exception except Exception as e: logger_server.critical(e, **{'exc_info': 1}) ctx.out_error = Fault('Server', get_fault_string_from_exception(e)) ctx.fire_event('method_exception_object') def call_wrapper(self, ctx): """This method calls the call_wrapper method in the service definition. This can be overridden to make an application-wide custom exception management. """ # no function if ctx.function is None: logger.debug("Skipping user code call as ctx.function is None.") return None # @rpc inside service class if ctx.descriptor.no_self: assert ctx.descriptor.service_class is not None return ctx.descriptor.service_class.call_wrapper(ctx) # from here on it's @mrpc in a (parent) class cls = ctx.descriptor.parent_class if cls.__orig__ is not None: cls = cls.__orig__ filters = {} inst = cls.__respawn__(ctx, filters) if inst is None: raise RespawnError( '{%s}%s with params %r' % (cls.get_namespace(), cls.get_type_name(), filters)) in_cls = ctx.descriptor.in_message args = tuple(ctx.in_object) if args is None: args = () elif ctx.descriptor.body_style is BODY_STYLE_WRAPPED and \ len(in_cls.get_flat_type_info(in_cls)) <= 1: args = () else: args = args[1:] # check whether this is a valid request according to the prerequisite # function (the callable that was passed in the _when argument to @mrpc) if ctx.descriptor.when is not None: if not ctx.descriptor.when(inst, ctx): raise InvalidRequestError("Invalid object state for request") if ctx.descriptor.no_ctx: args = (inst, ) + args else: args = ( inst, ctx, ) + args if ctx.descriptor.service_class is None: retval = ctx.function(*args) else: retval = ctx.descriptor.service_class.call_wrapper(ctx, args=args) return retval def _has_callbacks(self): return self.interface._has_callbacks() def reinitialize(self, server): """This is normally called on transport instantiation by ServerBase""" seen = set() from spyne import MethodDescriptor for d in self.interface.method_id_map.values(): assert isinstance(d, MethodDescriptor) if d.aux is not None and not id(d.aux) in seen: d.aux.initialize(server) seen.add(id(d.aux)) if d.service_class is not None and not id(d.service_class) in seen: d.service_class.initialize(server) seen.add(id(d.service_class)) def __hash__(self): return hash(tuple((id(s) for s in self.services))) def check_unique_method_keys(self): keys = {} #import ipdb; ipdb.set_trace() for s in self.services: for mdesc in s.public_methods.values(): other_mdesc = keys.get(mdesc.internal_key, None) if other_mdesc is not None: logger.error( 'Methods keys for "%s.%s" and "%s.%s" conflict', mdesc.function.__module__, six.get_function_name(mdesc.function), other_mdesc.function.__module__, six.get_function_name(other_mdesc.function)) raise MethodAlreadyExistsError(mdesc.internal_key) keys[mdesc.internal_key] = mdesc