[imp] improved logging, now blacklist openerplib
[odoo/odoo.git] / openerp / modules / registry.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 """ Models registries.
23
24 """
25 import threading
26
27 import logging
28
29 import openerp.sql_db
30 import openerp.osv.orm
31 import openerp.cron
32 import openerp.tools
33 import openerp.modules.db
34 import openerp.tools.config
35
36 class Registry(object):
37     """ Model registry for a particular database.
38
39     The registry is essentially a mapping between model names and model
40     instances. There is one registry instance per database.
41
42     """
43
44     def __init__(self, db_name):
45         self.models = {} # model name/model instance mapping
46         self._sql_error = {}
47         self._store_function = {}
48         self._init = True
49         self._init_parent = {}
50         self.db_name = db_name
51         self.db = openerp.sql_db.db_connect(db_name)
52
53         cr = self.db.cursor()
54         has_unaccent = openerp.modules.db.has_unaccent(cr)
55         if openerp.tools.config['unaccent'] and not has_unaccent:
56             logger = logging.getLogger('unaccent')
57             logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
58         self.has_unaccent = openerp.tools.config['unaccent'] and has_unaccent
59         cr.close()
60
61     def do_parent_store(self, cr):
62         for o in self._init_parent:
63             self.get(o)._parent_store_compute(cr)
64         self._init = False
65
66     def obj_list(self):
67         """ Return the list of model names in this registry."""
68         return self.models.keys()
69
70     def add(self, model_name, model):
71         """ Add or replace a model in the registry."""
72         self.models[model_name] = model
73
74     def get(self, model_name):
75         """ Return a model for a given name or None if it doesn't exist."""
76         return self.models.get(model_name)
77
78     def __getitem__(self, model_name):
79         """ Return a model for a given name or raise KeyError if it doesn't exist."""
80         return self.models[model_name]
81
82     def load(self, cr, module):
83         """ Load a given module in the registry.
84
85         At the Python level, the modules are already loaded, but not yet on a
86         per-registry level. This method populates a registry with the given
87         modules, i.e. it instanciates all the classes of a the given module
88         and registers them in the registry.
89
90         """
91
92         res = []
93
94         # Instantiate registered classes (via the MetaModel automatic discovery
95         # or via explicit constructor call), and add them to the pool.
96         for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
97             res.append(cls.create_instance(self, cr))
98
99         return res
100
101     def schedule_cron_jobs(self):
102         """ Make the cron thread care about this registry/database jobs.
103         This will initiate the cron thread to check for any pending jobs for
104         this registry/database as soon as possible. Then it will continuously
105         monitor the ir.cron model for future jobs. See openerp.cron for
106         details.
107         """
108         openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
109
110     def clear_caches(self):
111         """ Clear the caches
112         This clears the caches associated to methods decorated with
113         ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models.
114         """
115         for model in self.models.itervalues():
116             model.clear_caches()
117
118 class RegistryManager(object):
119     """ Model registries manager.
120
121         The manager is responsible for creation and deletion of model
122         registries (essentially database connection/model registry pairs).
123
124     """
125     # Mapping between db name and model registry.
126     # Accessed through the methods below.
127     registries = {}
128     registries_lock = threading.RLock()
129
130     @classmethod
131     def get(cls, db_name, force_demo=False, status=None, update_module=False,
132             pooljobs=True):
133         """ Return a registry for a given database name."""
134         with cls.registries_lock:
135             if db_name in cls.registries:
136                 registry = cls.registries[db_name]
137             else:
138                 registry = cls.new(db_name, force_demo, status,
139                                    update_module, pooljobs)
140             return registry
141
142     @classmethod
143     def new(cls, db_name, force_demo=False, status=None,
144             update_module=False, pooljobs=True):
145         """ Create and return a new registry for a given database name.
146
147         The (possibly) previous registry for that database name is discarded.
148
149         """
150         import openerp.modules
151         with cls.registries_lock:
152             registry = Registry(db_name)
153
154             # Initializing a registry will call general code which will in turn
155             # call registries.get (this object) to obtain the registry being
156             # initialized. Make it available in the registries dictionary then
157             # remove it if an exception is raised.
158             cls.delete(db_name)
159             cls.registries[db_name] = registry
160             try:
161                 # This should be a method on Registry
162                 openerp.modules.load_modules(registry.db, force_demo, status, update_module)
163             except Exception:
164                 del cls.registries[db_name]
165                 raise
166
167             cr = registry.db.cursor()
168             try:
169                 registry.do_parent_store(cr)
170                 registry.get('ir.actions.report.xml').register_all(cr)
171                 cr.commit()
172             finally:
173                 cr.close()
174
175             if pooljobs:
176                 registry.schedule_cron_jobs()
177
178             return registry
179
180     @classmethod
181     def delete(cls, db_name):
182         """Delete the registry linked to a given database.
183
184         This also cleans the associated caches. For good measure this also
185         cancels the associated cron job. But please note that the cron job can
186         be running and take some time before ending, and that you should not
187         remove a registry if it can still be used by some thread. So it might
188         be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
189         and join (i.e. wait for) the thread.
190         """
191         with cls.registries_lock:
192             if db_name in cls.registries:
193                 cls.registries[db_name].clear_caches()
194                 del cls.registries[db_name]
195                 openerp.cron.cancel(db_name)
196
197
198     @classmethod
199     def delete_all(cls):
200         """Delete all the registries. """
201         with cls.registries_lock:
202             for db_name in cls.registries.keys():
203                 cls.delete(db_name)
204
205     @classmethod
206     def clear_caches(cls, db_name):
207         """Clear caches
208
209         This clears the caches associated to methods decorated with
210         ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models
211         of the given database name.
212
213         This method is given to spare you a ``RegistryManager.get(db_name)``
214         that would loads the given database if it was not already loaded.
215         """
216         with cls.registries_lock:
217             if db_name in cls.registries:
218                 cls.registries[db_name].clear_caches()
219
220
221 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: