Merge lp:~openerp-dev/openobject-server/trunk-test_cursor-rco into lp:openobject-server

Proposed by Raphael Collet (OpenERP)
Status: Merged
Merged at revision: 5169
Proposed branch: lp:~openerp-dev/openobject-server/trunk-test_cursor-rco
Merge into: lp:openobject-server
Diff against target: 718 lines (+163/-123)
17 files modified
openerp/addons/base/res/res_users.py (+4/-5)
openerp/addons/base/tests/test_db_cursor.py (+1/-1)
openerp/addons/base/tests/test_ir_sequence.py (+1/-1)
openerp/addons/base/tests/test_uninstall.py (+1/-1)
openerp/addons/base/tests/test_views.py (+1/-1)
openerp/cli/server.py (+2/-2)
openerp/http.py (+5/-13)
openerp/modules/registry.py (+68/-32)
openerp/pooler.py (+2/-2)
openerp/service/model.py (+2/-13)
openerp/service/report.py (+2/-2)
openerp/sql_db.py (+57/-0)
openerp/tests/common.py (+13/-37)
openerp/tools/mail.py (+1/-1)
openerpcommand/module.py (+1/-4)
openerpcommand/read.py (+1/-4)
openerpcommand/uninstall.py (+1/-4)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/trunk-test_cursor-rco
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+214925@code.launchpad.net

Description of the change

Change implementation of the "test" cursor used mainly for js tests:
 - change the lock on registry to allow concurrent accesses during tests only
 - implement a "test" cursor that is used across multiple requests;
   this cursor simulates commit and rollback with a savepoint
 - always use a registry method to retrieve a cursor on the database;
   when in test mode, this method returns a test cursor and serializes requests
 - in unittest classes, make self.registry the registry (not just a function)

To post a comment you must log in.
Revision history for this message
Christophe Simonis (OpenERP) (kangol) wrote :

in file openerp/addons/base/tests/test_db_cursor.py, you can keep the context manager.
in file openerp/sql_db.py: in TestCursor() no need to ovewrite method execute to just call super()

more generally, I'm pretty sure there is still addons that call `registry.db.cursor()`.
To forbid that, can you rename the `db` variable to `__db` (it's private after all)

5169. By Raphael Collet (OpenERP)

[IMP] sql_db: simplify TestCursor, given that method execute() does not need overriding

5170. By Raphael Collet (OpenERP)

[IMP] registry: avoid every direct access registry.db, and rename attribute as registry._db

Revision history for this message
Raphael Collet (OpenERP) (rco-openerp) wrote :

openerp/addons/base/tests/test_db_cursor.py: done
openerp/sql_db.py: removed overriding of execute()

In Registry, I renamed registry.db into registry._db, and rewrote every call to registry.db.cursor().

5171. By Raphael Collet (OpenERP)

[FIX] registry: stupid typo

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openerp/addons/base/res/res_users.py'
--- openerp/addons/base/res/res_users.py 2014-04-07 12:50:50 +0000
+++ openerp/addons/base/res/res_users.py 2014-04-09 13:52:38 +0000
@@ -388,14 +388,13 @@
388 if not password:388 if not password:
389 return False389 return False
390 user_id = False390 user_id = False
391 cr = self.pool.db.cursor()391 cr = self.pool.cursor()
392 try:392 try:
393 # autocommit: our single update request will be performed atomically.393 # autocommit: our single update request will be performed atomically.
394 # (In this way, there is no opportunity to have two transactions394 # (In this way, there is no opportunity to have two transactions
395 # interleaving their cr.execute()..cr.commit() calls and have one395 # interleaving their cr.execute()..cr.commit() calls and have one
396 # of them rolled back due to a concurrent access.)396 # of them rolled back due to a concurrent access.)
397 if not openerp.tools.config['test_enable']:397 cr.autocommit(True)
398 cr.autocommit(True)
399 # check if user exists398 # check if user exists
400 res = self.search(cr, SUPERUSER_ID, [('login','=',login)])399 res = self.search(cr, SUPERUSER_ID, [('login','=',login)])
401 if res:400 if res:
@@ -440,7 +439,7 @@
440 # Successfully logged in as admin!439 # Successfully logged in as admin!
441 # Attempt to guess the web base url...440 # Attempt to guess the web base url...
442 if user_agent_env and user_agent_env.get('base_location'):441 if user_agent_env and user_agent_env.get('base_location'):
443 cr = self.pool.db.cursor()442 cr = self.pool.cursor()
444 try:443 try:
445 base = user_agent_env['base_location']444 base = user_agent_env['base_location']
446 ICP = self.pool['ir.config_parameter']445 ICP = self.pool['ir.config_parameter']
@@ -461,7 +460,7 @@
461 raise openerp.exceptions.AccessDenied()460 raise openerp.exceptions.AccessDenied()
462 if self._uid_cache.get(db, {}).get(uid) == passwd:461 if self._uid_cache.get(db, {}).get(uid) == passwd:
463 return462 return
464 cr = self.pool.db.cursor()463 cr = self.pool.cursor()
465 try:464 try:
466 self.check_credentials(cr, uid, passwd)465 self.check_credentials(cr, uid, passwd)
467 if self._uid_cache.has_key(db):466 if self._uid_cache.has_key(db):
468467
=== modified file 'openerp/addons/base/tests/test_db_cursor.py'
--- openerp/addons/base/tests/test_db_cursor.py 2014-02-09 00:37:45 +0000
+++ openerp/addons/base/tests/test_db_cursor.py 2014-04-09 13:52:38 +0000
@@ -21,7 +21,7 @@
21 """21 """
22 Try to use iterable but non-list or int params in query parameters.22 Try to use iterable but non-list or int params in query parameters.
23 """23 """
24 with registry().cursor(auto_commit=False) as cr:24 with registry().cursor() as cr:
25 with self.assertRaises(ValueError):25 with self.assertRaises(ValueError):
26 cr.execute("SELECT id FROM res_users WHERE login=%s", 'admin')26 cr.execute("SELECT id FROM res_users WHERE login=%s", 'admin')
27 with self.assertRaises(ValueError):27 with self.assertRaises(ValueError):
2828
=== modified file 'openerp/addons/base/tests/test_ir_sequence.py'
--- openerp/addons/base/tests/test_ir_sequence.py 2014-03-18 12:41:12 +0000
+++ openerp/addons/base/tests/test_ir_sequence.py 2014-04-09 13:52:38 +0000
@@ -21,7 +21,7 @@
21 return openerp.modules.registry.RegistryManager.get(DB)[model]21 return openerp.modules.registry.RegistryManager.get(DB)[model]
2222
23def cursor():23def cursor():
24 return openerp.modules.registry.RegistryManager.get(DB).db.cursor()24 return openerp.modules.registry.RegistryManager.get(DB).cursor()
2525
2626
27def drop_sequence(code):27def drop_sequence(code):
2828
=== modified file 'openerp/addons/base/tests/test_uninstall.py'
--- openerp/addons/base/tests/test_uninstall.py 2014-02-09 00:37:45 +0000
+++ openerp/addons/base/tests/test_uninstall.py 2014-04-09 13:52:38 +0000
@@ -13,7 +13,7 @@
13 return openerp.modules.registry.RegistryManager.get(DB)[model]13 return openerp.modules.registry.RegistryManager.get(DB)[model]
1414
15def cursor():15def cursor():
16 return openerp.modules.registry.RegistryManager.get(DB).db.cursor()16 return openerp.modules.registry.RegistryManager.get(DB).cursor()
1717
18def get_module(module_name):18def get_module(module_name):
19 registry = openerp.modules.registry.RegistryManager.get(DB)19 registry = openerp.modules.registry.RegistryManager.get(DB)
2020
=== modified file 'openerp/addons/base/tests/test_views.py'
--- openerp/addons/base/tests/test_views.py 2014-02-25 11:47:06 +0000
+++ openerp/addons/base/tests/test_views.py 2014-04-09 13:52:38 +0000
@@ -28,7 +28,7 @@
28 self.assertTreesEqual(c1, c2, msg)28 self.assertTreesEqual(c1, c2, msg)
2929
3030
31class TestNodeLocator(common.BaseCase):31class TestNodeLocator(common.TransactionCase):
32 """32 """
33 The node locator returns None when it can not find a node, and the first33 The node locator returns None when it can not find a node, and the first
34 match when it finds something (no jquery-style node sets)34 match when it finds something (no jquery-style node sets)
3535
=== modified file 'openerp/cli/server.py'
--- openerp/cli/server.py 2014-04-07 16:05:48 +0000
+++ openerp/cli/server.py 2014-04-09 13:52:38 +0000
@@ -111,7 +111,7 @@
111 fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower()111 fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower()
112 buf = file(config["translate_out"], "w")112 buf = file(config["translate_out"], "w")
113 registry = openerp.modules.registry.RegistryManager.new(dbname)113 registry = openerp.modules.registry.RegistryManager.new(dbname)
114 cr = registry.db.cursor()114 cr = registry.cursor()
115 openerp.tools.trans_export(config["language"],115 openerp.tools.trans_export(config["language"],
116 config["translate_modules"] or ["all"], buf, fileformat, cr)116 config["translate_modules"] or ["all"], buf, fileformat, cr)
117 cr.close()117 cr.close()
@@ -125,7 +125,7 @@
125 dbname = config['db_name']125 dbname = config['db_name']
126126
127 registry = openerp.modules.registry.RegistryManager.new(dbname)127 registry = openerp.modules.registry.RegistryManager.new(dbname)
128 cr = registry.db.cursor()128 cr = registry.cursor()
129 openerp.tools.trans_load( cr, config["translate_in"], config["language"],129 openerp.tools.trans_load( cr, config["translate_in"], config["language"],
130 context=context)130 context=context)
131 cr.commit()131 cr.commit()
132132
=== modified file 'openerp/http.py'
--- openerp/http.py 2014-04-07 16:05:48 +0000
+++ openerp/http.py 2014-04-09 13:52:38 +0000
@@ -235,10 +235,7 @@
235 """235 """
236 # some magic to lazy create the cr236 # some magic to lazy create the cr
237 if not self._cr:237 if not self._cr:
238 # Test cursors238 self._cr = self.registry.cursor()
239 self._cr = openerp.tests.common.acquire_test_cursor(self.session_id)
240 if not self._cr:
241 self._cr = self.registry.db.cursor()
242 return self._cr239 return self._cr
243240
244 def __enter__(self):241 def __enter__(self):
@@ -249,14 +246,9 @@
249 _request_stack.pop()246 _request_stack.pop()
250247
251 if self._cr:248 if self._cr:
252 # Dont close test cursors249 if exc_type is None and not self._failed:
253 if not openerp.tests.common.release_test_cursor(self._cr):250 self._cr.commit()
254 if exc_type is None and not self._failed:251 self._cr.close()
255 self._cr.commit()
256 else:
257 # just to be explicit - happens at close() anyway
258 self._cr.rollback()
259 self._cr.close()
260 # just to be sure no one tries to re-use the request252 # just to be sure no one tries to re-use the request
261 self.disable_db = True253 self.disable_db = True
262 self.uid = None254 self.uid = None
@@ -294,7 +286,7 @@
294 def checked_call(___dbname, *a, **kw):286 def checked_call(___dbname, *a, **kw):
295 # The decorator can call us more than once if there is an database error. In this287 # The decorator can call us more than once if there is an database error. In this
296 # case, the request cursor is unusable. Rollback transaction to create a new one.288 # case, the request cursor is unusable. Rollback transaction to create a new one.
297 if self._cr and not openerp.tools.config['test_enable']:289 if self._cr:
298 self._cr.rollback()290 self._cr.rollback()
299 return self.endpoint(*a, **kw)291 return self.endpoint(*a, **kw)
300292
301293
=== modified file 'openerp/modules/registry.py'
--- openerp/modules/registry.py 2014-02-16 21:22:22 +0000
+++ openerp/modules/registry.py 2014-04-09 13:52:38 +0000
@@ -58,7 +58,10 @@
58 self._init_modules = set()58 self._init_modules = set()
5959
60 self.db_name = db_name60 self.db_name = db_name
61 self.db = openerp.sql_db.db_connect(db_name)61 self._db = openerp.sql_db.db_connect(db_name)
62
63 # special cursor for test mode; None means "normal" mode
64 self.test_cr = None
6265
63 # Indicates that the registry is 66 # Indicates that the registry is
64 self.ready = False67 self.ready = False
@@ -75,7 +78,7 @@
75 # Useful only in a multi-process context.78 # Useful only in a multi-process context.
76 self._any_cache_cleared = False79 self._any_cache_cleared = False
7780
78 cr = self.db.cursor()81 cr = self.cursor()
79 has_unaccent = openerp.modules.db.has_unaccent(cr)82 has_unaccent = openerp.modules.db.has_unaccent(cr)
80 if openerp.tools.config['unaccent'] and not has_unaccent:83 if openerp.tools.config['unaccent'] and not has_unaccent:
81 _logger.warning("The option --unaccent was given but no unaccent() function was found in database.")84 _logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
@@ -102,6 +105,10 @@
102 """ Return the model with the given name or raise KeyError if it doesn't exist."""105 """ Return the model with the given name or raise KeyError if it doesn't exist."""
103 return self.models[model_name]106 return self.models[model_name]
104107
108 def __call__(self, model_name):
109 """ Same as ``self[model_name]``. """
110 return self.models[model_name]
111
105 def do_parent_store(self, cr):112 def do_parent_store(self, cr):
106 for o in self._init_parent:113 for o in self._init_parent:
107 self.get(o)._parent_store_compute(cr)114 self.get(o)._parent_store_compute(cr)
@@ -183,27 +190,38 @@
183 r, c)190 r, c)
184 return r, c191 return r, c
185192
186 @contextmanager193 def enter_test_mode(self):
187 def cursor(self, auto_commit=True):194 """ Enter the 'test' mode, where one cursor serves several requests. """
188 cr = self.db.cursor()195 assert self.test_cr is None
189 try:196 self.test_cr = self._db.test_cursor()
190 yield cr197 RegistryManager.enter_test_mode()
191 if auto_commit:198
192 cr.commit()199 def leave_test_mode(self):
193 finally:200 """ Leave the test mode. """
194 cr.close()201 assert self.test_cr is not None
195202 self.test_cr.close(force=True) # close the cursor for real
196class TestRLock(object):203 self.test_cr = None
197 def __init__(self):204 RegistryManager.leave_test_mode()
198 self._lock = threading.RLock()205
206 def cursor(self):
207 """ Return a new cursor for the database. The cursor itself may be used
208 as a context manager to commit/rollback and close automatically.
209 """
210 if self.test_cr is not None:
211 # While in test mode, we use one special cursor across requests. The
212 # test cursor uses a reentrant lock to serialize accesses. The lock
213 # is granted here by cursor(), and automatically released by the
214 # cursor itself in its method close().
215 self.test_cr.acquire()
216 return self.test_cr
217 return self._db.cursor()
218
219class DummyRLock(object):
220 """ Dummy reentrant lock, to be used while running rpc and js tests """
199 def acquire(self):221 def acquire(self):
200 if openerp.tools.config['test_enable']:222 pass
201 return
202 return self._lock.acquire()
203 def release(self):223 def release(self):
204 if openerp.tools.config['test_enable']:224 pass
205 return
206 return self._lock.release()
207 def __enter__(self):225 def __enter__(self):
208 self.acquire()226 self.acquire()
209 def __exit__(self, type, value, traceback):227 def __exit__(self, type, value, traceback):
@@ -219,12 +237,30 @@
219 # Mapping between db name and model registry.237 # Mapping between db name and model registry.
220 # Accessed through the methods below.238 # Accessed through the methods below.
221 registries = {}239 registries = {}
222 registries_lock = TestRLock()240 _lock = threading.RLock()
241 _saved_lock = None
242
243 @classmethod
244 def lock(cls):
245 """ Return the current registry lock. """
246 return cls._lock
247
248 @classmethod
249 def enter_test_mode(cls):
250 """ Enter the 'test' mode, where the registry is no longer locked. """
251 assert cls._saved_lock is None
252 cls._lock, cls._saved_lock = DummyRLock(), cls._lock
253
254 @classmethod
255 def leave_test_mode(cls):
256 """ Leave the 'test' mode. """
257 assert cls._saved_lock is not None
258 cls._lock, cls._saved_lock = cls._saved_lock, None
223259
224 @classmethod260 @classmethod
225 def get(cls, db_name, force_demo=False, status=None, update_module=False):261 def get(cls, db_name, force_demo=False, status=None, update_module=False):
226 """ Return a registry for a given database name."""262 """ Return a registry for a given database name."""
227 with cls.registries_lock:263 with cls.lock():
228 try:264 try:
229 return cls.registries[db_name]265 return cls.registries[db_name]
230 except KeyError:266 except KeyError:
@@ -244,7 +280,7 @@
244280
245 """281 """
246 import openerp.modules282 import openerp.modules
247 with cls.registries_lock:283 with cls.lock():
248 registry = Registry(db_name)284 registry = Registry(db_name)
249285
250 # Initializing a registry will call general code which will in turn286 # Initializing a registry will call general code which will in turn
@@ -259,7 +295,7 @@
259 registry.base_registry_signaling_sequence = seq_registry295 registry.base_registry_signaling_sequence = seq_registry
260 registry.base_cache_signaling_sequence = seq_cache296 registry.base_cache_signaling_sequence = seq_cache
261 # This should be a method on Registry297 # This should be a method on Registry
262 openerp.modules.load_modules(registry.db, force_demo, status, update_module)298 openerp.modules.load_modules(registry._db, force_demo, status, update_module)
263 except Exception:299 except Exception:
264 del cls.registries[db_name]300 del cls.registries[db_name]
265 raise301 raise
@@ -269,7 +305,7 @@
269 # Yeah, crazy.305 # Yeah, crazy.
270 registry = cls.registries[db_name]306 registry = cls.registries[db_name]
271307
272 cr = registry.db.cursor()308 cr = registry.cursor()
273 try:309 try:
274 registry.do_parent_store(cr)310 registry.do_parent_store(cr)
275 cr.commit()311 cr.commit()
@@ -286,7 +322,7 @@
286 @classmethod322 @classmethod
287 def delete(cls, db_name):323 def delete(cls, db_name):
288 """Delete the registry linked to a given database. """324 """Delete the registry linked to a given database. """
289 with cls.registries_lock:325 with cls.lock():
290 if db_name in cls.registries:326 if db_name in cls.registries:
291 cls.registries[db_name].clear_caches()327 cls.registries[db_name].clear_caches()
292 del cls.registries[db_name]328 del cls.registries[db_name]
@@ -294,7 +330,7 @@
294 @classmethod330 @classmethod
295 def delete_all(cls):331 def delete_all(cls):
296 """Delete all the registries. """332 """Delete all the registries. """
297 with cls.registries_lock:333 with cls.lock():
298 for db_name in cls.registries.keys():334 for db_name in cls.registries.keys():
299 cls.delete(db_name)335 cls.delete(db_name)
300336
@@ -309,7 +345,7 @@
309 This method is given to spare you a ``RegistryManager.get(db_name)``345 This method is given to spare you a ``RegistryManager.get(db_name)``
310 that would loads the given database if it was not already loaded.346 that would loads the given database if it was not already loaded.
311 """347 """
312 with cls.registries_lock:348 with cls.lock():
313 if db_name in cls.registries:349 if db_name in cls.registries:
314 cls.registries[db_name].clear_caches()350 cls.registries[db_name].clear_caches()
315351
@@ -325,7 +361,7 @@
325 changed = False361 changed = False
326 if openerp.multi_process and db_name in cls.registries:362 if openerp.multi_process and db_name in cls.registries:
327 registry = cls.get(db_name)363 registry = cls.get(db_name)
328 cr = registry.db.cursor()364 cr = registry.cursor()
329 try:365 try:
330 cr.execute("""366 cr.execute("""
331 SELECT base_registry_signaling.last_value,367 SELECT base_registry_signaling.last_value,
@@ -371,7 +407,7 @@
371 registry = cls.get(db_name)407 registry = cls.get(db_name)
372 if registry.any_cache_cleared():408 if registry.any_cache_cleared():
373 _logger.info("At least one model cache has been cleared, signaling through the database.")409 _logger.info("At least one model cache has been cleared, signaling through the database.")
374 cr = registry.db.cursor()410 cr = registry.cursor()
375 r = 1411 r = 1
376 try:412 try:
377 cr.execute("select nextval('base_cache_signaling')")413 cr.execute("select nextval('base_cache_signaling')")
@@ -386,7 +422,7 @@
386 if openerp.multi_process and db_name in cls.registries:422 if openerp.multi_process and db_name in cls.registries:
387 _logger.info("Registry changed, signaling through the database")423 _logger.info("Registry changed, signaling through the database")
388 registry = cls.get(db_name)424 registry = cls.get(db_name)
389 cr = registry.db.cursor()425 cr = registry.cursor()
390 r = 1426 r = 1
391 try:427 try:
392 cr.execute("select nextval('base_registry_signaling')")428 cr.execute("select nextval('base_registry_signaling')")
393429
=== modified file 'openerp/pooler.py'
--- openerp/pooler.py 2013-03-27 14:16:53 +0000
+++ openerp/pooler.py 2014-04-09 13:52:38 +0000
@@ -36,7 +36,7 @@
36 assert openerp.conf.deprecation.openerp_pooler36 assert openerp.conf.deprecation.openerp_pooler
37 _logger.warning('openerp.pooler.get_db_and_pool() is deprecated.')37 _logger.warning('openerp.pooler.get_db_and_pool() is deprecated.')
38 registry = RegistryManager.get(db_name, force_demo, status, update_module)38 registry = RegistryManager.get(db_name, force_demo, status, update_module)
39 return registry.db, registry39 return registry._db, registry
4040
4141
42def restart_pool(db_name, force_demo=False, status=None, update_module=False):42def restart_pool(db_name, force_demo=False, status=None, update_module=False):
@@ -44,7 +44,7 @@
44 _logger.warning('openerp.pooler.restart_pool() is deprecated.')44 _logger.warning('openerp.pooler.restart_pool() is deprecated.')
45 assert openerp.conf.deprecation.openerp_pooler45 assert openerp.conf.deprecation.openerp_pooler
46 registry = RegistryManager.new(db_name, force_demo, status, update_module)46 registry = RegistryManager.new(db_name, force_demo, status, update_module)
47 return registry.db, registry47 return registry._db, registry
4848
49def get_db(db_name):49def get_db(db_name):
50 """Return a database connection. The corresponding registry is initialized."""50 """Return a database connection. The corresponding registry is initialized."""
5151
=== modified file 'openerp/service/model.py'
--- openerp/service/model.py 2014-02-09 00:40:05 +0000
+++ openerp/service/model.py 2014-04-09 13:52:38 +0000
@@ -161,21 +161,10 @@
161def execute_kw(db, uid, obj, method, args, kw=None):161def execute_kw(db, uid, obj, method, args, kw=None):
162 return execute(db, uid, obj, method, *args, **kw or {})162 return execute(db, uid, obj, method, *args, **kw or {})
163163
164@contextmanager
165def closing_cr_and_commit(cr):
166 try:
167 yield cr
168 cr.commit()
169 except Exception:
170 cr.rollback()
171 raise
172 finally:
173 cr.close()
174
175@check164@check
176def execute(db, uid, obj, method, *args, **kw):165def execute(db, uid, obj, method, *args, **kw):
177 threading.currentThread().dbname = db166 threading.currentThread().dbname = db
178 with closing_cr_and_commit(openerp.registry(db).db.cursor()) as cr:167 with openerp.registry(db).cursor() as cr:
179 if method.startswith('_'):168 if method.startswith('_'):
180 raise except_orm('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))169 raise except_orm('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
181 res = execute_cr(cr, uid, obj, method, *args, **kw)170 res = execute_cr(cr, uid, obj, method, *args, **kw)
@@ -190,7 +179,7 @@
190179
191@check180@check
192def exec_workflow(db, uid, obj, signal, *args):181def exec_workflow(db, uid, obj, signal, *args):
193 with closing_cr_and_commit(openerp.registry(db).db.cursor()) as cr:182 with openerp.registry(db).cursor() as cr:
194 return exec_workflow_cr(cr, uid, obj, signal, *args)183 return exec_workflow_cr(cr, uid, obj, signal, *args)
195184
196# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:185# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
197186
=== modified file 'openerp/service/report.py'
--- openerp/service/report.py 2013-03-27 16:40:45 +0000
+++ openerp/service/report.py 2014-04-09 13:52:38 +0000
@@ -49,7 +49,7 @@
4949
50 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}50 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}
5151
52 cr = openerp.registry(db).db.cursor()52 cr = openerp.registry(db).cursor()
53 try:53 try:
54 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)54 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)
55 if not result:55 if not result:
@@ -87,7 +87,7 @@
87 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}87 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}
8888
89 def go(id, uid, ids, datas, context):89 def go(id, uid, ids, datas, context):
90 cr = openerp.registry(db).db.cursor()90 cr = openerp.registry(db).cursor()
91 try:91 try:
92 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)92 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)
93 if not result:93 if not result:
9494
=== modified file 'openerp/sql_db.py'
--- openerp/sql_db.py 2014-03-17 12:43:43 +0000
+++ openerp/sql_db.py 2014-04-09 13:52:38 +0000
@@ -347,6 +347,23 @@
347 """347 """
348 return self._cnx.rollback()348 return self._cnx.rollback()
349349
350 def __enter__(self):
351 """ Using the cursor as a contextmanager automatically commits and
352 closes it::
353
354 with cr:
355 cr.execute(...)
356
357 # cr is committed if no failure occurred
358 # cr is closed in any case
359 """
360 return self
361
362 def __exit__(self, exc_type, exc_value, traceback):
363 if exc_type is None:
364 self.commit()
365 self.close()
366
350 @contextmanager367 @contextmanager
351 @check368 @check
352 def savepoint(self):369 def savepoint(self):
@@ -364,6 +381,41 @@
364 def __getattr__(self, name):381 def __getattr__(self, name):
365 return getattr(self._obj, name)382 return getattr(self._obj, name)
366383
384class TestCursor(Cursor):
385 """ A cursor to be used for tests. It keeps the transaction open across
386 several requests, and simulates committing, rolling back, and closing.
387 """
388 def __init__(self, *args, **kwargs):
389 # in order to simulate commit and rollback, the cursor maintains a
390 # savepoint at its last commit
391 super(TestCursor, self).__init__(*args, **kwargs)
392 self.execute("SAVEPOINT test_cursor")
393 self._lock = threading.RLock()
394
395 def acquire(self):
396 self._lock.acquire()
397
398 def release(self):
399 self._lock.release()
400
401 def close(self, force=False):
402 if force:
403 super(TestCursor, self).close()
404 elif not self._closed:
405 self.rollback() # for stuff that has not been committed
406 self.release()
407
408 def autocommit(self, on):
409 _logger.debug("TestCursor.autocommit(%r) does nothing", on)
410
411 def commit(self):
412 self.execute("RELEASE SAVEPOINT test_cursor")
413 self.execute("SAVEPOINT test_cursor")
414
415 def rollback(self):
416 self.execute("ROLLBACK TO SAVEPOINT test_cursor")
417 self.execute("SAVEPOINT test_cursor")
418
367class PsycoConnection(psycopg2.extensions.connection):419class PsycoConnection(psycopg2.extensions.connection):
368 pass420 pass
369421
@@ -491,6 +543,11 @@
491 _logger.debug('create %scursor to %r', cursor_type, self.dbname)543 _logger.debug('create %scursor to %r', cursor_type, self.dbname)
492 return Cursor(self._pool, self.dbname, serialized=serialized)544 return Cursor(self._pool, self.dbname, serialized=serialized)
493545
546 def test_cursor(self, serialized=True):
547 cursor_type = serialized and 'serialized ' or ''
548 _logger.debug('create test %scursor to %r', cursor_type, self.dbname)
549 return TestCursor(self._pool, self.dbname, serialized=serialized)
550
494 # serialized_cursor is deprecated - cursors are serialized by default551 # serialized_cursor is deprecated - cursors are serialized by default
495 serialized_cursor = cursor552 serialized_cursor = cursor
496553
497554
=== modified file 'openerp/tests/common.py'
--- openerp/tests/common.py 2014-04-03 13:41:09 +0000
+++ openerp/tests/common.py 2014-04-09 13:52:38 +0000
@@ -20,6 +20,7 @@
20import werkzeug20import werkzeug
2121
22import openerp22import openerp
23from openerp.modules.registry import RegistryManager
2324
24_logger = logging.getLogger(__name__)25_logger = logging.getLogger(__name__)
2526
@@ -37,25 +38,6 @@
37# Useless constant, tests are aware of the content of demo data38# Useless constant, tests are aware of the content of demo data
38ADMIN_USER_ID = openerp.SUPERUSER_ID39ADMIN_USER_ID = openerp.SUPERUSER_ID
3940
40# Magic session_id, unfortunately we have to serialize access to the cursors to
41# serialize requests. We first tried to duplicate the database for each tests
42# but this proved too slow. Any idea to improve this is welcome.
43HTTP_SESSION = {}
44
45def acquire_test_cursor(session_id):
46 if openerp.tools.config['test_enable']:
47 cr = HTTP_SESSION.get(session_id)
48 if cr:
49 cr._test_lock.acquire()
50 return cr
51
52def release_test_cursor(cr):
53 if openerp.tools.config['test_enable']:
54 if hasattr(cr, '_test_lock'):
55 cr._test_lock.release()
56 return True
57 return False
58
59def at_install(flag):41def at_install(flag):
60 """ Sets the at-install state of a test, the flag is a boolean specifying42 """ Sets the at-install state of a test, the flag is a boolean specifying
61 whether the test should (``True``) or should not (``False``) run during43 whether the test should (``True``) or should not (``False``) run during
@@ -67,6 +49,7 @@
67 obj.at_install = flag49 obj.at_install = flag
68 return obj50 return obj
69 return decorator51 return decorator
52
70def post_install(flag):53def post_install(flag):
71 """ Sets the post-install state of a test. The flag is a boolean54 """ Sets the post-install state of a test. The flag is a boolean
72 specifying whether the test should or should not run after a set of55 specifying whether the test should or should not run after a set of
@@ -83,18 +66,13 @@
83 """66 """
84 Subclass of TestCase for common OpenERP-specific code.67 Subclass of TestCase for common OpenERP-specific code.
85 68
86 This class is abstract and expects self.cr and self.uid to be initialized by subclasses.69 This class is abstract and expects self.registry, self.cr and self.uid to be
70 initialized by subclasses.
87 """71 """
8872
89 @classmethod
90 def cursor(self):73 def cursor(self):
91 return openerp.modules.registry.RegistryManager.get(DB).db.cursor()74 return self.registry.cursor()
9275
93 @classmethod
94 def registry(self, model):
95 return openerp.modules.registry.RegistryManager.get(DB)[model]
96
97 @classmethod
98 def ref(self, xid):76 def ref(self, xid):
99 """ Returns database ID corresponding to a given identifier.77 """ Returns database ID corresponding to a given identifier.
10078
@@ -106,7 +84,6 @@
106 _, id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, module, xid)84 _, id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, module, xid)
107 return id85 return id
10886
109 @classmethod
110 def browse_ref(self, xid):87 def browse_ref(self, xid):
111 """ Returns a browsable record for the given identifier.88 """ Returns a browsable record for the given identifier.
11289
@@ -125,10 +102,9 @@
125 """102 """
126103
127 def setUp(self):104 def setUp(self):
128 # Store cr and uid in class variables, to allow ref() and browse_ref to be BaseCase @classmethods105 self.registry = RegistryManager.get(DB)
129 # and still access them106 self.cr = self.cursor()
130 TransactionCase.cr = self.cursor()107 self.uid = openerp.SUPERUSER_ID
131 TransactionCase.uid = openerp.SUPERUSER_ID
132108
133 def tearDown(self):109 def tearDown(self):
134 self.cr.rollback()110 self.cr.rollback()
@@ -143,7 +119,8 @@
143119
144 @classmethod120 @classmethod
145 def setUpClass(cls):121 def setUpClass(cls):
146 cls.cr = cls.cursor()122 cls.registry = RegistryManager.get(DB)
123 cls.cr = cls.registry.cursor()
147 cls.uid = openerp.SUPERUSER_ID124 cls.uid = openerp.SUPERUSER_ID
148125
149 @classmethod126 @classmethod
@@ -166,16 +143,15 @@
166143
167 def setUp(self):144 def setUp(self):
168 super(HttpCase, self).setUp()145 super(HttpCase, self).setUp()
146 self.registry.enter_test_mode()
169 # setup a magic session_id that will be rollbacked147 # setup a magic session_id that will be rollbacked
170 self.session = openerp.http.root.session_store.new()148 self.session = openerp.http.root.session_store.new()
171 self.session_id = self.session.sid149 self.session_id = self.session.sid
172 self.session.db = DB150 self.session.db = DB
173 openerp.http.root.session_store.save(self.session)151 openerp.http.root.session_store.save(self.session)
174 self.cr._test_lock = threading.RLock()
175 HTTP_SESSION[self.session_id] = self.cr
176152
177 def tearDown(self):153 def tearDown(self):
178 del HTTP_SESSION[self.session_id]154 self.registry.leave_test_mode()
179 super(HttpCase, self).tearDown()155 super(HttpCase, self).tearDown()
180156
181 def url_open(self, url, data=None, timeout=10):157 def url_open(self, url, data=None, timeout=10):
182158
=== modified file 'openerp/tools/mail.py'
--- openerp/tools/mail.py 2014-02-19 14:06:17 +0000
+++ openerp/tools/mail.py 2014-04-09 13:52:38 +0000
@@ -625,7 +625,7 @@
625 if not cr:625 if not cr:
626 db_name = getattr(threading.currentThread(), 'dbname', None)626 db_name = getattr(threading.currentThread(), 'dbname', None)
627 if db_name:627 if db_name:
628 local_cr = cr = openerp.registry(db_name).db.cursor()628 local_cr = cr = openerp.registry(db_name).cursor()
629 else:629 else:
630 raise Exception("No database cursor found, please pass one explicitly")630 raise Exception("No database cursor found, please pass one explicitly")
631631
632632
=== modified file 'openerpcommand/module.py'
--- openerpcommand/module.py 2013-01-11 13:46:57 +0000
+++ openerpcommand/module.py 2014-04-09 13:52:38 +0000
@@ -33,12 +33,9 @@
33 33
34 xs = []34 xs = []
35 ir_module_module = registry.get('ir.module.module')35 ir_module_module = registry.get('ir.module.module')
36 cr = registry.db.cursor() # TODO context manager36 with registry.cursor() as cr:
37 try:
38 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [], {})37 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [], {})
39 xs = ir_module_module.read(cr, openerp.SUPERUSER_ID, ids, [], {})38 xs = ir_module_module.read(cr, openerp.SUPERUSER_ID, ids, [], {})
40 finally:
41 cr.close()
42 39
43 if xs:40 if xs:
44 print "Modules (database `%s`):" % (args.database,)41 print "Modules (database `%s`):" % (args.database,)
4542
=== modified file 'openerpcommand/read.py'
--- openerpcommand/read.py 2013-03-29 14:07:23 +0000
+++ openerpcommand/read.py 2014-04-09 13:52:38 +0000
@@ -20,15 +20,12 @@
20 registry = openerp.modules.registry.RegistryManager.get(20 registry = openerp.modules.registry.RegistryManager.get(
21 args.database, update_module=False)21 args.database, update_module=False)
22 model = registry[args.model]22 model = registry[args.model]
23 cr = registry.db.cursor() # TODO context manager
24 field_names = [args.field] if args.field else []23 field_names = [args.field] if args.field else []
25 if args.short:24 if args.short:
26 # ignore --field25 # ignore --field
27 field_names = ['name']26 field_names = ['name']
28 try:27 with registry.cursor() as cr:
29 xs = model.read(cr, 1, args.id, field_names, {})28 xs = model.read(cr, 1, args.id, field_names, {})
30 finally:
31 cr.close()
3229
33 if xs:30 if xs:
34 print "Records (model `%s`, database `%s`):" % (args.model, args.database)31 print "Records (model `%s`, database `%s`):" % (args.model, args.database)
3532
=== modified file 'openerpcommand/uninstall.py'
--- openerpcommand/uninstall.py 2013-02-09 06:02:46 +0000
+++ openerpcommand/uninstall.py 2014-04-09 13:52:38 +0000
@@ -43,15 +43,12 @@
43 args.database, update_module=False)43 args.database, update_module=False)
4444
45 ir_module_module = registry.get('ir.module.module')45 ir_module_module = registry.get('ir.module.module')
46 cr = registry.db.cursor() # TODO context manager46 with registry.cursor() as cr:
47 try:
48 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [('name', 'in', args.module), ('state', '=', 'installed')], {})47 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [('name', 'in', args.module), ('state', '=', 'installed')], {})
49 if len(ids) == len(args.module):48 if len(ids) == len(args.module):
50 ir_module_module.button_immediate_uninstall(cr, openerp.SUPERUSER_ID, ids, {})49 ir_module_module.button_immediate_uninstall(cr, openerp.SUPERUSER_ID, ids, {})
51 else:50 else:
52 print "At least one module not found (database `%s`)." % (args.database,)51 print "At least one module not found (database `%s`)." % (args.database,)
53 finally:
54 cr.close()
5552
56def add_parser(subparsers):53def add_parser(subparsers):
57 parser = subparsers.add_parser('uninstall',54 parser = subparsers.add_parser('uninstall',