[IMP] [MOV] Moved test_osv and test_translate from unchecked test directory to tests...
[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 from contextlib import contextmanager
26 import logging
27 import threading
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 from openerp.tools import assertion_report
36
37 _logger = logging.getLogger(__name__)
38
39 class Registry(object):
40     """ Model registry for a particular database.
41
42     The registry is essentially a mapping between model names and model
43     instances. There is one registry instance per database.
44
45     """
46
47     def __init__(self, db_name):
48         self.models = {}    # model name/model instance mapping
49         self._sql_error = {}
50         self._store_function = {}
51         self._init = True
52         self._init_parent = {}
53         self._assertion_report = assertion_report.assertion_report()
54
55         # modules fully loaded (maintained during init phase by `loading` module)
56         self._init_modules = set()
57
58         self.db_name = db_name
59         self.db = openerp.sql_db.db_connect(db_name)
60
61         cr = self.db.cursor()
62         has_unaccent = openerp.modules.db.has_unaccent(cr)
63         if openerp.tools.config['unaccent'] and not has_unaccent:
64             _logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
65         self.has_unaccent = openerp.tools.config['unaccent'] and has_unaccent
66         cr.close()
67
68     def do_parent_store(self, cr):
69         for o in self._init_parent:
70             self.get(o)._parent_store_compute(cr)
71         self._init = False
72
73     def obj_list(self):
74         """ Return the list of model names in this registry."""
75         return self.models.keys()
76
77     def add(self, model_name, model):
78         """ Add or replace a model in the registry."""
79         self.models[model_name] = model
80
81     def get(self, model_name):
82         """ Return a model for a given name or None if it doesn't exist."""
83         return self.models.get(model_name)
84
85     def __getitem__(self, model_name):
86         """ Return a model for a given name or raise KeyError if it doesn't exist."""
87         return self.models[model_name]
88
89     def load(self, cr, module):
90         """ Load a given module in the registry.
91
92         At the Python level, the modules are already loaded, but not yet on a
93         per-registry level. This method populates a registry with the given
94         modules, i.e. it instanciates all the classes of a the given module
95         and registers them in the registry.
96
97         """
98
99         res = []
100
101         # Instantiate registered classes (via the MetaModel automatic discovery
102         # or via explicit constructor call), and add them to the pool.
103         for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
104             res.append(cls.create_instance(self, cr))
105
106         return res
107
108     def schedule_cron_jobs(self):
109         """ Make the cron thread care about this registry/database jobs.
110         This will initiate the cron thread to check for any pending jobs for
111         this registry/database as soon as possible. Then it will continuously
112         monitor the ir.cron model for future jobs. See openerp.cron for
113         details.
114         """
115         openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
116
117     def clear_caches(self):
118         """ Clear the caches
119         This clears the caches associated to methods decorated with
120         ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models.
121         """
122         for model in self.models.itervalues():
123             model.clear_caches()
124
125     @contextmanager
126     def cursor(self, auto_commit=True):
127         cr = self.db.cursor()
128         try:
129             yield cr
130             if auto_commit:
131                 cr.commit()
132         finally:
133             cr.close()
134
135
136 class RegistryManager(object):
137     """ Model registries manager.
138
139         The manager is responsible for creation and deletion of model
140         registries (essentially database connection/model registry pairs).
141
142     """
143     # Mapping between db name and model registry.
144     # Accessed through the methods below.
145     registries = {}
146     registries_lock = threading.RLock()
147
148     @classmethod
149     def get(cls, db_name, force_demo=False, status=None, update_module=False,
150             pooljobs=True):
151         """ Return a registry for a given database name."""
152         try:
153             return cls.registries[db_name]
154         except KeyError:
155             return cls.new(db_name, force_demo, status,
156                            update_module, pooljobs)
157
158     @classmethod
159     def new(cls, db_name, force_demo=False, status=None,
160             update_module=False, pooljobs=True):
161         """ Create and return a new registry for a given database name.
162
163         The (possibly) previous registry for that database name is discarded.
164
165         """
166         import openerp.modules
167         with cls.registries_lock:
168             registry = Registry(db_name)
169
170             # Initializing a registry will call general code which will in turn
171             # call registries.get (this object) to obtain the registry being
172             # initialized. Make it available in the registries dictionary then
173             # remove it if an exception is raised.
174             cls.delete(db_name)
175             cls.registries[db_name] = registry
176             try:
177                 # This should be a method on Registry
178                 openerp.modules.load_modules(registry.db, force_demo, status, update_module)
179             except Exception:
180                 del cls.registries[db_name]
181                 raise
182
183             cr = registry.db.cursor()
184             try:
185                 registry.do_parent_store(cr)
186                 registry.get('ir.actions.report.xml').register_all(cr)
187                 cr.commit()
188             finally:
189                 cr.close()
190
191         if pooljobs:
192             registry.schedule_cron_jobs()
193
194         return registry
195
196     @classmethod
197     def delete(cls, db_name):
198         """Delete the registry linked to a given database.
199
200         This also cleans the associated caches. For good measure this also
201         cancels the associated cron job. But please note that the cron job can
202         be running and take some time before ending, and that you should not
203         remove a registry if it can still be used by some thread. So it might
204         be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
205         and join (i.e. wait for) the thread.
206         """
207         with cls.registries_lock:
208             if db_name in cls.registries:
209                 cls.registries[db_name].clear_caches()
210                 del cls.registries[db_name]
211                 openerp.cron.cancel(db_name)
212
213     @classmethod
214     def delete_all(cls):
215         """Delete all the registries. """
216         with cls.registries_lock:
217             for db_name in cls.registries.keys():
218                 cls.delete(db_name)
219
220     @classmethod
221     def clear_caches(cls, db_name):
222         """Clear caches
223
224         This clears the caches associated to methods decorated with
225         ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models
226         of the given database name.
227
228         This method is given to spare you a ``RegistryManager.get(db_name)``
229         that would loads the given database if it was not already loaded.
230         """
231         with cls.registries_lock:
232             if db_name in cls.registries:
233                 cls.registries[db_name].clear_caches()
234
235
236 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: