1 2# 3# spyne - Copyright (C) Spyne contributors. 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This library is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this library; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 18# 19 20""" 21This module contains the :class:`Service` class and its helper objects. 22""" 23 24import logging 25logger = logging.getLogger(__name__) 26 27from spyne.util.six.moves.collections_abc import Sequence 28 29from spyne.evmgr import EventManager 30from spyne.util import six 31from spyne.util.oset import oset 32 33 34class ServiceBaseMeta(type): 35 """Adds event managers.""" 36 37 def __init__(self, cls_name, cls_bases, cls_dict): 38 super(ServiceBaseMeta, self).__init__(cls_name, cls_bases, cls_dict) 39 40 self.public_methods = {} 41 self.event_manager = EventManager(self, 42 self.__get_base_event_handlers(cls_bases)) 43 44 def __get_base_event_handlers(self, cls_bases): 45 handlers = {} 46 47 for base in cls_bases: 48 evmgr = getattr(base, 'event_manager', None) 49 if evmgr is None: 50 continue 51 52 for k, v in evmgr.handlers.items(): 53 handler = handlers.get(k, oset()) 54 for h in v: 55 handler.add(h) 56 handlers[k] = handler 57 58 return handlers 59 60class ServiceMeta(ServiceBaseMeta): 61 """Creates the :class:`spyne.MethodDescriptor` objects by iterating over 62 tagged methods. 63 """ 64 65 def __init__(self, cls_name, cls_bases, cls_dict): 66 super(ServiceMeta, self).__init__(cls_name, cls_bases, cls_dict) 67 68 self.__has_aux_methods = self.__aux__ is not None 69 has_nonaux_methods = None 70 71 for k, v in cls_dict.items(): 72 if not hasattr(v, '_is_rpc'): 73 continue 74 75 descriptor = v(_default_function_name=k, _service_class=self) 76 77 # these two lines are needed for staticmethod wrapping to work 78 setattr(self, k, staticmethod(descriptor.function)) 79 descriptor.reset_function(getattr(self, k)) 80 81 try: 82 getattr(self, k).descriptor = descriptor 83 except AttributeError: 84 pass 85 # FIXME: this fails with builtins. Temporary hack while we 86 # investigate whether we really need this or not 87 88 self.public_methods[k] = descriptor 89 if descriptor.aux is None and self.__aux__ is None: 90 has_nonaux_methods = True 91 else: 92 self.__has_aux_methods = True 93 94 if self.__has_aux_methods and has_nonaux_methods: 95 raise Exception("You can't mix primary and " 96 "auxiliary methods in a single service definition.") 97 98 def is_auxiliary(self): 99 return self.__has_aux_methods 100 101 102# FIXME: To be renamed to ServiceBase in Spyne 3 103@six.add_metaclass(ServiceBaseMeta) 104class ServiceBaseBase(object): 105 __in_header__ = None 106 """The incoming header object that the methods under this service definition 107 accept.""" 108 109 __out_header__ = None 110 """The outgoing header object that the methods under this service definition 111 accept.""" 112 113 __service_name__ = None 114 """The name of this service definition as exposed in the interface document. 115 Defaults to the class name.""" 116 117 __service_module__ = None 118 """This is used for internal idenfitication of the service class, 119 to override the ``__module__`` attribute.""" 120 121 __port_types__ = () 122 """WSDL-Specific portType mappings""" 123 124 __aux__ = None 125 """The auxiliary method type. When set, the ``aux`` property of every method 126 defined under this service is set to this value. The _aux flag in the @srpc 127 decorator overrides this.""" 128 129 @classmethod 130 def get_service_class_name(cls): 131 return cls.__name__ 132 133 @classmethod 134 def get_service_name(cls): 135 if cls.__service_name__ is None: 136 return cls.__name__ 137 else: 138 return cls.__service_name__ 139 140 @classmethod 141 def get_service_module(cls): 142 if cls.__service_module__ is None: 143 return cls.__module__ 144 else: 145 return cls.__service_module__ 146 147 @classmethod 148 def get_internal_key(cls): 149 return "%s.%s" % (cls.get_service_module(), cls.get_service_name()) 150 151 @classmethod 152 def get_port_types(cls): 153 return cls.__port_types__ 154 155 @classmethod 156 def _has_callbacks(cls): 157 """Determines if this service definition has callback methods or not.""" 158 159 for method in cls.public_methods.values(): 160 if method.is_callback: 161 return True 162 163 return False 164 165 @classmethod 166 def get_context(cls): 167 """Returns a user defined context. Override this in your ServiceBase 168 subclass to customize context generation.""" 169 return None 170 171 @classmethod 172 def call_wrapper(cls, ctx, args=None): 173 """Called in place of the original method call. You can override this to 174 do your own exception handling. 175 176 :param ctx: The method context. 177 178 The overriding function must call this function by convention. 179 """ 180 181 if ctx.function is not None: 182 if args is None: 183 args = ctx.in_object 184 185 assert not isinstance(args, six.string_types) 186 187 # python3 wants a proper sequence as *args 188 if not isinstance(args, Sequence): 189 args = tuple(args) 190 191 if not ctx.descriptor.no_ctx: 192 args = (ctx,) + tuple(args) 193 194 return ctx.function(*args) 195 196 @classmethod 197 def initialize(cls, app): 198 pass 199 200 201@six.add_metaclass(ServiceMeta) 202class Service(ServiceBaseBase): 203 """The ``Service`` class is the base class for all service definitions. 204 205 The convention is to have public methods defined under a subclass of this 206 class along with common properties of public methods like header classes or 207 auxiliary processors. The :func:`spyne.decorator.srpc` decorator or its 208 wrappers should be used to flag public methods. 209 210 This class is designed to be subclassed just once. You're supposed to 211 combine Service subclasses in order to get the public method mix you 212 want. 213 214 It is a natural abstract base class, because it's of no use without any 215 method definitions, hence the 'Base' suffix in the name. 216 217 This class supports the following events: 218 * ``method_call`` 219 Called right before the service method is executed 220 221 * ``method_return_object`` 222 Called right after the service method is executed 223 224 * ``method_exception_object`` 225 Called when an exception occurred in a service method, before the 226 exception is serialized. 227 228 * ``method_accept_document`` 229 Called by the transport right after the incoming stream is parsed to 230 the incoming protocol's document type. 231 232 * ``method_return_document`` 233 Called by the transport right after the outgoing object is 234 serialized to the outgoing protocol's document type. 235 236 * ``method_exception_document`` 237 Called by the transport right before the outgoing exception object 238 is serialized to the outgoing protocol's document type. 239 240 * ``method_return_string`` 241 Called by the transport right before passing the return string to 242 the client. 243 244 * ``method_exception_string`` 245 Called by the transport right before passing the exception string to 246 the client. 247 """ 248 249 250# FIXME: To be deleted in Spyne 3 251ServiceBase = Service 252