Files
tryton/trytond/pool.py
T

235 lines
7.0 KiB
Python
Raw Normal View History

2014-12-02 12:54:01 +01:00
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2009-02-11 14:52:22 +01:00
from threading import RLock
import logging
2011-07-03 13:20:58 +02:00
from trytond.modules import load_modules, register_classes
from trytond.transaction import Transaction
import __builtin__
2009-02-11 14:52:22 +01:00
2015-10-01 22:37:13 +02:00
__all__ = ['Pool', 'PoolMeta', 'PoolBase', 'isregisteredby']
2012-09-24 22:16:53 +02:00
2015-03-02 23:39:15 +01:00
logger = logging.getLogger(__name__)
2012-09-24 22:16:53 +02:00
class PoolMeta(type):
def __new__(cls, name, bases, dct):
new = type.__new__(cls, name, bases, dct)
if '__name__' in dct:
2014-11-10 23:30:49 +01:00
try:
new.__name__ = dct['__name__']
except TypeError:
new.__name__ = dct['__name__'].encode('utf-8')
2012-09-24 22:16:53 +02:00
return new
2009-02-11 14:52:22 +01:00
class PoolBase(object):
__metaclass__ = PoolMeta
@classmethod
def __setup__(cls):
pass
@classmethod
def __post_setup__(cls):
pass
@classmethod
def __register__(cls, module_name):
pass
2009-02-11 14:52:22 +01:00
class Pool(object):
classes = {
'model': {},
'wizard': {},
'report': {},
}
_started = False
2009-02-11 14:52:22 +01:00
_lock = RLock()
_locks = {}
_pool = {}
2010-03-04 14:02:27 +01:00
test = False
2011-07-03 13:20:58 +02:00
_instances = {}
2009-02-11 14:52:22 +01:00
2011-07-03 13:20:58 +02:00
def __new__(cls, database_name=None):
if database_name is None:
database_name = Transaction().database.name
2011-07-03 13:20:58 +02:00
result = cls._instances.get(database_name)
if result:
return result
lock = cls._locks.get(database_name)
if not lock:
with cls._lock:
lock = cls._locks.setdefault(database_name, RLock())
with lock:
return cls._instances.setdefault(database_name,
super(Pool, cls).__new__(cls))
2009-02-11 14:52:22 +01:00
2011-07-03 13:20:58 +02:00
def __init__(self, database_name=None):
if database_name is None:
database_name = Transaction().database.name
2009-02-11 14:52:22 +01:00
self.database_name = database_name
@staticmethod
2012-09-24 22:16:53 +02:00
def register(*classes, **kwargs):
2009-02-11 14:52:22 +01:00
'''
2012-09-24 22:16:53 +02:00
Register a list of classes
2009-02-11 14:52:22 +01:00
'''
2012-09-24 22:16:53 +02:00
module = kwargs['module']
type_ = kwargs['type_']
assert type_ in ('model', 'report', 'wizard')
for cls in classes:
mpool = Pool.classes[type_].setdefault(module, [])
assert cls not in mpool, cls
assert issubclass(cls.__class__, PoolMeta), cls
2012-09-24 22:16:53 +02:00
mpool.append(cls)
2009-02-11 14:52:22 +01:00
2011-07-03 13:20:58 +02:00
@classmethod
def start(cls):
2009-08-07 10:42:23 +02:00
'''
Start/restart the Pool
'''
2011-07-03 13:20:58 +02:00
with cls._lock:
2012-09-24 22:16:53 +02:00
for classes in Pool.classes.itervalues():
classes.clear()
register_classes()
cls._started = True
2009-08-07 10:42:23 +02:00
2011-07-03 13:20:58 +02:00
@classmethod
def stop(cls, database_name):
'''
Stop the Pool
'''
2011-07-03 13:20:58 +02:00
with cls._lock:
if database_name in cls._instances:
del cls._instances[database_name]
2011-09-19 14:49:56 +02:00
lock = cls._locks.get(database_name)
if not lock:
return
with lock:
2011-07-03 13:20:58 +02:00
if database_name in cls._pool:
del cls._pool[database_name]
2011-07-03 13:20:58 +02:00
@classmethod
def database_list(cls):
2009-02-11 14:52:22 +01:00
'''
:return: database list
'''
with cls._lock:
databases = []
for database in cls._pool.keys():
if cls._locks.get(database):
if cls._locks[database].acquire(False):
databases.append(database)
cls._locks[database].release()
return databases
2009-02-11 14:52:22 +01:00
@property
def lock(self):
'''
Return the database lock for the pool.
'''
return self._locks[self.database_name]
def init(self, update=None, lang=None):
2009-02-11 14:52:22 +01:00
'''
Init pool
Set update to proceed to update
lang is a list of language code to be updated
2009-02-11 14:52:22 +01:00
'''
with self._lock:
if not self._started:
self.start()
2011-07-03 13:20:58 +02:00
with self._locks[self.database_name]:
# Don't reset pool if already init and not to update
if not update and self._pool.get(self.database_name):
return
2015-03-05 11:32:18 +01:00
logger.info('init pool for "%s"', self.database_name)
2011-07-03 13:20:58 +02:00
self._pool.setdefault(self.database_name, {})
2014-12-02 12:54:01 +01:00
# Clean the _pool before loading modules
2009-02-11 14:52:22 +01:00
for type in self.classes.keys():
self._pool[self.database_name][type] = {}
restart = not load_modules(self.database_name, self, update=update,
lang=lang)
if restart:
self.init()
def get(self, name, type='model'):
'''
Get an object from the pool
:param name: the object name
:param type: the type
:return: the instance
'''
if type == '*':
for type in self.classes.keys():
if name in self._pool[self.database_name][type]:
break
try:
return self._pool[self.database_name][type][name]
except KeyError:
if type == 'report':
from trytond.report import Report
# Keyword argument 'type' conflicts with builtin function
cls = __builtin__.type(str(name), (Report,), {})
2012-09-24 22:16:53 +02:00
cls.__setup__()
self.add(cls, type)
return cls
raise
2009-02-11 14:52:22 +01:00
2012-09-24 22:16:53 +02:00
def add(self, cls, type='model'):
2009-02-11 14:52:22 +01:00
'''
2012-09-24 22:16:53 +02:00
Add a classe to the pool
2009-02-11 14:52:22 +01:00
'''
2011-07-03 13:20:58 +02:00
with self._locks[self.database_name]:
2012-09-24 22:16:53 +02:00
self._pool[self.database_name][type][cls.__name__] = cls
2009-02-11 14:52:22 +01:00
def iterobject(self, type='model'):
'''
Return an iterator over object name, object
:param type: the type
:return: an iterator
'''
return self._pool[self.database_name][type].iteritems()
2016-03-17 22:56:58 +01:00
def fill(self, module):
2009-02-11 14:52:22 +01:00
'''
2016-03-17 22:56:58 +01:00
Fill the pool with the registered class from the module.
Return a list of classes for each type in a dictionary.
2009-02-11 14:52:22 +01:00
'''
2012-09-24 22:16:53 +02:00
classes = {}
for type_ in self.classes.keys():
classes[type_] = []
for cls in self.classes[type_].get(module, []):
try:
previous_cls = self.get(cls.__name__, type=type_)
cls = type(cls.__name__, (cls, previous_cls), {})
except KeyError:
pass
if not issubclass(cls, PoolBase):
continue
2012-09-24 22:16:53 +02:00
self.add(cls, type=type_)
classes[type_].append(cls)
return classes
2015-10-01 22:37:13 +02:00
2016-03-17 22:56:58 +01:00
def setup(self, classes=None):
logger.info('setup pool for "%s"', self.database_name)
if classes is None:
classes = {}
for type_ in self._pool[self.database_name]:
classes[type_] = self._pool[self.database_name][type_].values()
for type_, lst in classes.iteritems():
for cls in lst:
cls.__setup__()
for cls in lst:
cls.__post_setup__()
2015-10-01 22:37:13 +02:00
def isregisteredby(obj, module, type_='model'):
pool = Pool()
classes = pool.classes[type_]
return any(issubclass(obj, cls) for cls in classes.get(module, []))