Revision 4ab1af1a snf-common/synnefo/lib/pool/__init__.py

b/snf-common/synnefo/lib/pool/__init__.py
196 196
        log.debug("PUT-AFTER: finished putting object %r back to pool %r",
197 197
                  obj, self)
198 198

  
199
    def pool_create_free(self):
200
        """Create a free new object that is not put into the pool.
201

  
202
        Just for convenience, let the users create objects with
203
        the exact same configuration as those that are used with the pool
204

  
205
        """
206
        obj = self._pool_create_free()
207
        return obj
208

  
209
    def _pool_create_free(self):
210
        """Create a free new object that is not put into the pool.
211

  
212
        This should be overriden by pool classes.
213
        Otherwise, it just calls _pool_create().
214

  
215
        """
216
        return self._pool_create()
217

  
199 218
    def _pool_create(self):
200 219
        """Create a new object to be used with this pool.
201 220

  
202 221
        Create a new object to be used with this pool,
203 222
        should be overriden in subclasses.
204 223
        Must be thread-safe.
224

  
205 225
        """
206 226
        raise NotImplementedError
207 227

  
......
226 246

  
227 247
        """
228 248
        raise NotImplementedError
249

  
250

  
251
class PooledObject(object):
252
    """Generic Object Pool context manager and pooled object proxy.
253

  
254
    The PooledObject instance acts as a context manager to
255
    be used in a with statement:
256

  
257
        with PooledObject(...) as obj:
258
            use(obj)
259

  
260
    The with block above is roughly equivalent to:
261

  
262
        pooled = PooledObject(...):
263
        try:
264
            obj = pooled.acquire()
265
            assert(obj is pooled.obj)
266
            use(obj)
267
        finally:
268
            pooled.release()
269

  
270
    After exiting the with block, or releasing,
271
    the code MUST not use the obj again in any way.
272

  
273
    """
274

  
275
    # NOTE: We need all definitions at class-level
276
    # to avoid infinite __gettatr__() recursion.
277
    # This is also true for subclasses.
278

  
279
    # NOTE: Typically you will only need to override
280
    #       __init__() and get_pool
281

  
282
    # Initialization. Do not customize.
283
    _pool_settings = None
284
    _pool_get_settings = None
285
    _pool_kwargs = None
286
    _pool = None
287
    obj = None
288

  
289
    #####################################################
290
    ### Subclass attribute customization begins here. ###
291

  
292
    _pool_log_prefix = "POOL"
293
    _pool_class = ObjectPool
294

  
295
    # default keyword args to pass to pool initialization
296
    _pool_default_settings = (
297
            ('size', 25),
298
        )
299

  
300
    # keyword args to pass to pool_get
301
    _pool_default_get_settings = (
302
            ('blocking', True),
303
            #('timeout', None),
304
            ('create', True),
305
            ('verify', True),
306
        )
307

  
308
    # behavior settings
309
    _pool_attach_context = False
310
    _pool_disable_after_release = True
311
    _pool_ignore_double_release = False
312

  
313
    ###  Subclass attribute customization ends here.  ###
314
    #####################################################
315

  
316
    def __init__(self, pool_settings=None,
317
                       get_settings=None,
318
                       attach_context=None,
319
                       disable_after_release=None,
320
                       ignore_double_release=None,
321
                       **kwargs):
322
        """Initialize a PooledObject instance.
323

  
324
        Accept only keyword arguments.
325
        Some of them are filtered for this instance's configuration,
326
        and the rest are saved in ._pool_kwargs for later use.
327

  
328
        The filtered keywords are:
329

  
330
        pool_settings:  keyword args forwarded to pool instance initialization
331
                        in get_pool(), on top of the class defaults.
332
                        If not given, the remaining keyword args are
333
                        forwarded instead.
334

  
335
        get_settings:   keyword args forwarded to the pool's .pool_get() on top
336
                        of the class defaults.
337

  
338
        attach_context: boolean overriding the class default.
339
                        If True, after getting an object from the pool,
340
                        attach self onto it before returning it,
341
                        so that the context manager caller can have
342
                        access to the manager object within the with: block.
343

  
344
        disable_after_release:
345
                        boolean overriding the class default.
346
                        If True, the PooledObject will not allow a second
347
                        acquisition after the first release. For example,
348
                        the second with will raise an AssertionError:
349
                        manager = PooledObject()
350
                        with manager as c:
351
                            pass
352
                        with manager as c:
353
                            pass
354

  
355
        ignore_double_release:
356
                        boolean overriding the class default.
357
                        If True, the PooledObject will allow consecutive
358
                        calls to release the underlying pooled object.
359
                        Only the first one has an effect.
360
                        If False, an AssertionError is raised.
361

  
362
        """
363
        self._pool_kwargs = kwargs
364
        self._pool = None
365
        self.obj = None
366

  
367
        _get_settings = dict(self._pool_default_get_settings)
368
        if get_settings is not None:
369
            _get_settings.update(get_settings)
370
        self._pool_get_settings = _get_settings
371

  
372
        if attach_context is not None:
373
            self._pool_attach_context = attach_context
374

  
375
        if pool_settings is None:
376
            pool_settings = kwargs
377

  
378
        _pool_settings = dict(self._pool_default_settings)
379
        _pool_settings.update(**pool_settings)
380
        self._pool_settings = _pool_settings
381

  
382
    def get_pool(self):
383
        """Return a suitable pool object to work with.
384

  
385
        Called within .acquire(), it is meant to be
386
        overriden by sublasses, to create a new pool,
387
        or retrieve an existing one, based on the PooledObject
388
        initialization keywords stored in self._pool_kwargs.
389

  
390
        """
391
        pool = self._pool_class(**self._pool_settings)
392
        return pool
393

  
394
    ### Maybe overriding get_pool() and __init__() above is enough ###
395

  
396
    def __repr__(self):
397
        return ("<object %s of class %s: "
398
                "proxy for object (%r) in pool (%r)>" % (
399
                id(self), self.__class__.__name__,
400
                self.obj, self._pool))
401

  
402
    __str__ = __repr__
403

  
404
    ## Proxy the real object. Disabled until needed.
405
    ##
406
    ##def __getattr__(self, name):
407
    ##    return getattr(self.obj, name)
408

  
409
    ##def __setattr__(self, name, value):
410
    ##    if hasattr(self, name):
411
    ##        _setattr = super(PooledObject, self).__setattr__
412
    ##        _setattr(name, value)
413
    ##    else:
414
    ##        setattr(self.obj, name, value)
415

  
416
    ##def __delattr_(self, name):
417
    ##    _delattr = super(PooledObject, self).__delattr__
418
    ##    if hasattr(self, name):
419
    ##        _delattr(self, name)
420
    ##    else:
421
    ##        delattr(self.obj, name)
422

  
423
    def __enter__(self):
424
        return self.acquire()
425

  
426
    def __exit__(self, exc_type, exc_value, trace):
427
        return self.release()
428

  
429
    def acquire(self):
430
        log.debug("%s Acquiring (context: %r)", self._pool_log_prefix, self)
431
        pool = self._pool
432
        if pool is False:
433
            m = "%r: has been released. No further pool access allowed." % (
434
                self,)
435
            raise AssertionError(m)
436
        if pool is not None:
437
            m = "Double acquire in %r" % self
438
            raise AssertionError(m)
439

  
440
        pool = self.get_pool()
441
        self._pool = pool
442

  
443
        obj = pool.pool_get(**self._pool_get_settings)
444
        if self._pool_attach_context:
445
            obj._pool_context = self
446
        self.obj = obj
447
        log.debug("%s Acquired %r", self._pool_log_prefix, obj)
448
        return obj
449

  
450
    def release(self):
451
        log.debug("%s Releasing (context: %r)", self._pool_log_prefix, self)
452
        pool = self._pool
453
        if pool is None:
454
            m = "%r: no pool" % (self,)
455
            raise AssertionError(m)
456

  
457
        obj = self.obj
458
        if obj is None:
459
            if self._pool_ignore_double_release:
460
                return
461
            m = "%r: no object. Double release?" % (self,)
462
            raise AssertionError(m)
463

  
464
        pool.pool_put(obj)
465
        self.obj = None
466
        if self._pool_disable_after_release:
467
            self._pool = False

Also available in: Unified diff