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