Package wtf :: Module services
[hide private]
[frames] | no frames]

Source Code for Module wtf.services

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006-2016 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 17  """ 
 18  Service Loading and Initialization 
 19  ================================== 
 20   
 21  This module provides for service loading and initialization. 
 22  """ 
 23  __author__ = u"Andr\xe9 Malo" 
 24  __docformat__ = "restructuredtext en" 
 25   
 26  from wtf import Error, WtfWarning 
 27  from wtf import util as _util 
 28   
 29  _global_services = {}  # pylint: disable = invalid-name 
 30   
 31   
32 -def global_service(name):
33 """ 34 Find global service by name 35 36 :Return: Service module or ``None`` 37 """ 38 return _global_services.get(name)
39 40
41 -class ServiceError(Error):
42 """ Service intialization failure """
43 44
45 -class ServiceInterfaceWarning(WtfWarning):
46 """ Service interface warning """
47 48
49 -class ServiceInterface(object):
50 """ 51 Interface for global and local services, initialized at startup time 52 """ 53
54 - def __init__(self, config, opts, args):
55 """ 56 Initialization 57 58 :Parameters: 59 - `config`: Configuration 60 - `opts`: Command line options 61 - `args`: Positioned command line arguments 62 63 :Types: 64 - `config`: `wtf.config.Config` 65 - `opts`: ``optparse.OptionContainer`` 66 - `args`: ``list`` 67 """
68
69 - def shutdown(self):
70 """ 71 Shutdown the service 72 73 This method is called when the services are no longer needed. 74 It can be used to release external resources etc in a clean way. 75 """
76
77 - def global_service(self):
78 """ 79 Return the global service object 80 81 If there's no global service provided, the method is expected to 82 return ``None`` 83 84 :return: A tuple containing the global object the service provides 85 and the name which the object will be stored under in the 86 service module (``('name', any)``) 87 :rtype: ``tuple`` 88 """
89
90 - def middleware(self, func):
91 """ 92 Middleware factory 93 94 :Parameters: 95 - `func`: The function to wrap (WSGI compatible callable) 96 97 :Types: 98 - `func`: ``callable`` 99 100 :return: A WSGI callable. If the service does not 101 provide a WSGI middleware, the `func` argument should just 102 be returned, the initialized middleware (wrapping `func`) 103 otherwise. 104 :rtype: ``callable`` 105 """
106 107
108 -class ServiceManager(object):
109 """ 110 Service manager 111 112 :IVariables: 113 - `_finalized`: Manager was finalized 114 - `_down`: Manager was shut down 115 - `_services`: List of services 116 117 :Types: 118 - `_finalized`: ``bool`` 119 - `_down`: ``bool`` 120 - `_services`: ``list`` 121 """ 122 _finalized, _down, _services = False, False, () 123
124 - def __init__(self):
125 """ Initialization """ 126 self._services = [] 127 self._globals = {}
128
129 - def __del__(self):
130 """ Destruction """ 131 self.shutdown()
132
133 - def finalize(self):
134 """ Lock the manager. No more services can be added """ 135 self._services.reverse() 136 self._finalized = True
137
138 - def add(self, service):
139 """ Add a new service """ 140 assert not self._finalized, "ServiceManager was already finalized" 141 self._services.append(service)
142
143 - def apply(self, app):
144 """ 145 Apply the middlewares to the application 146 147 :Parameters: 148 - `app`: The WSGI application callable to wrap 149 150 :Types: 151 - `app`: ``callable`` 152 153 :return: Wrapped application (if there are middlewares to apply, the 154 original callable otherwise) 155 :rtype: ``callable`` 156 """ 157 assert self._finalized, "ServiceManager was not finalized yet" 158 assert not self._down, "ServiceManager was already shutdown" 159 for service in self._services: 160 app = service.middleware(app) 161 return app
162
163 - def shutdown(self):
164 """ Shutdown the services """ 165 self._down = True 166 services, self._services = self._services, [] 167 for service in services: 168 try: 169 func = service.shutdown 170 except AttributeError: 171 ServiceInterfaceWarning.emit( 172 "Missing 'shutdown' method for service %r" % (service,) 173 ) 174 else: 175 func()
176 177
178 -def init(config, opts, args, services, module='__svc__'):
179 """ 180 Initialize services 181 182 The function can only be called once (because the module will be only 183 initialized once) 184 185 :Parameters: 186 - `config`: Configuration 187 - `opts`: Command line options 188 - `args`: Positioned command line arguments 189 - `services`: List of services to initialize. The list items can either 190 be classes (which are instanciated) or strings containing dotted class 191 names (which will be loaded and instanciated). Service classes must 192 implement the `ServiceInterface`. 193 - `module`: Dotted module name, where global services are put into 194 195 :Types: 196 - `config`: `wtf.config.Config` 197 - `opts`: ``optparse.OptionContainer`` 198 - `args`: ``list`` 199 - `services`: ``iterable`` 200 - `module`: ``str`` 201 202 :return: Service manager 203 :rtype: `ServiceManager` 204 """ 205 _, fresh = _util.make_dotted(module) 206 assert fresh, "Services already initialized" 207 208 module, manager = module.split('.'), ServiceManager() 209 for service in services: 210 if isinstance(service, basestring): 211 service = _util.load_dotted(str(service)) 212 service = service(config, opts, args) 213 manager.add(service) 214 svc = service.global_service() 215 if svc is not None: 216 fullname, svc = svc 217 name = module + fullname.split('.') 218 if len(name) > 1: 219 (prename, _), name = _util.make_dotted( 220 '.'.join(name[:-1])), name[-1] 221 if getattr(prename, name, None) is not None: 222 raise ServiceError("%s.%s already exists" % (prename, name)) 223 setattr(prename, name, svc) 224 _global_services[fullname] = svc 225 226 manager.finalize() 227 return manager
228