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
1=== modified file 'openerp/addons/base/res/res_users.py'
2--- openerp/addons/base/res/res_users.py 2014-04-07 12:50:50 +0000
3+++ openerp/addons/base/res/res_users.py 2014-04-09 13:52:38 +0000
4@@ -388,14 +388,13 @@
5 if not password:
6 return False
7 user_id = False
8- cr = self.pool.db.cursor()
9+ cr = self.pool.cursor()
10 try:
11 # autocommit: our single update request will be performed atomically.
12 # (In this way, there is no opportunity to have two transactions
13 # interleaving their cr.execute()..cr.commit() calls and have one
14 # of them rolled back due to a concurrent access.)
15- if not openerp.tools.config['test_enable']:
16- cr.autocommit(True)
17+ cr.autocommit(True)
18 # check if user exists
19 res = self.search(cr, SUPERUSER_ID, [('login','=',login)])
20 if res:
21@@ -440,7 +439,7 @@
22 # Successfully logged in as admin!
23 # Attempt to guess the web base url...
24 if user_agent_env and user_agent_env.get('base_location'):
25- cr = self.pool.db.cursor()
26+ cr = self.pool.cursor()
27 try:
28 base = user_agent_env['base_location']
29 ICP = self.pool['ir.config_parameter']
30@@ -461,7 +460,7 @@
31 raise openerp.exceptions.AccessDenied()
32 if self._uid_cache.get(db, {}).get(uid) == passwd:
33 return
34- cr = self.pool.db.cursor()
35+ cr = self.pool.cursor()
36 try:
37 self.check_credentials(cr, uid, passwd)
38 if self._uid_cache.has_key(db):
39
40=== modified file 'openerp/addons/base/tests/test_db_cursor.py'
41--- openerp/addons/base/tests/test_db_cursor.py 2014-02-09 00:37:45 +0000
42+++ openerp/addons/base/tests/test_db_cursor.py 2014-04-09 13:52:38 +0000
43@@ -21,7 +21,7 @@
44 """
45 Try to use iterable but non-list or int params in query parameters.
46 """
47- with registry().cursor(auto_commit=False) as cr:
48+ with registry().cursor() as cr:
49 with self.assertRaises(ValueError):
50 cr.execute("SELECT id FROM res_users WHERE login=%s", 'admin')
51 with self.assertRaises(ValueError):
52
53=== modified file 'openerp/addons/base/tests/test_ir_sequence.py'
54--- openerp/addons/base/tests/test_ir_sequence.py 2014-03-18 12:41:12 +0000
55+++ openerp/addons/base/tests/test_ir_sequence.py 2014-04-09 13:52:38 +0000
56@@ -21,7 +21,7 @@
57 return openerp.modules.registry.RegistryManager.get(DB)[model]
58
59 def cursor():
60- return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
61+ return openerp.modules.registry.RegistryManager.get(DB).cursor()
62
63
64 def drop_sequence(code):
65
66=== modified file 'openerp/addons/base/tests/test_uninstall.py'
67--- openerp/addons/base/tests/test_uninstall.py 2014-02-09 00:37:45 +0000
68+++ openerp/addons/base/tests/test_uninstall.py 2014-04-09 13:52:38 +0000
69@@ -13,7 +13,7 @@
70 return openerp.modules.registry.RegistryManager.get(DB)[model]
71
72 def cursor():
73- return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
74+ return openerp.modules.registry.RegistryManager.get(DB).cursor()
75
76 def get_module(module_name):
77 registry = openerp.modules.registry.RegistryManager.get(DB)
78
79=== modified file 'openerp/addons/base/tests/test_views.py'
80--- openerp/addons/base/tests/test_views.py 2014-02-25 11:47:06 +0000
81+++ openerp/addons/base/tests/test_views.py 2014-04-09 13:52:38 +0000
82@@ -28,7 +28,7 @@
83 self.assertTreesEqual(c1, c2, msg)
84
85
86-class TestNodeLocator(common.BaseCase):
87+class TestNodeLocator(common.TransactionCase):
88 """
89 The node locator returns None when it can not find a node, and the first
90 match when it finds something (no jquery-style node sets)
91
92=== modified file 'openerp/cli/server.py'
93--- openerp/cli/server.py 2014-04-07 16:05:48 +0000
94+++ openerp/cli/server.py 2014-04-09 13:52:38 +0000
95@@ -111,7 +111,7 @@
96 fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower()
97 buf = file(config["translate_out"], "w")
98 registry = openerp.modules.registry.RegistryManager.new(dbname)
99- cr = registry.db.cursor()
100+ cr = registry.cursor()
101 openerp.tools.trans_export(config["language"],
102 config["translate_modules"] or ["all"], buf, fileformat, cr)
103 cr.close()
104@@ -125,7 +125,7 @@
105 dbname = config['db_name']
106
107 registry = openerp.modules.registry.RegistryManager.new(dbname)
108- cr = registry.db.cursor()
109+ cr = registry.cursor()
110 openerp.tools.trans_load( cr, config["translate_in"], config["language"],
111 context=context)
112 cr.commit()
113
114=== modified file 'openerp/http.py'
115--- openerp/http.py 2014-04-07 16:05:48 +0000
116+++ openerp/http.py 2014-04-09 13:52:38 +0000
117@@ -235,10 +235,7 @@
118 """
119 # some magic to lazy create the cr
120 if not self._cr:
121- # Test cursors
122- self._cr = openerp.tests.common.acquire_test_cursor(self.session_id)
123- if not self._cr:
124- self._cr = self.registry.db.cursor()
125+ self._cr = self.registry.cursor()
126 return self._cr
127
128 def __enter__(self):
129@@ -249,14 +246,9 @@
130 _request_stack.pop()
131
132 if self._cr:
133- # Dont close test cursors
134- if not openerp.tests.common.release_test_cursor(self._cr):
135- if exc_type is None and not self._failed:
136- self._cr.commit()
137- else:
138- # just to be explicit - happens at close() anyway
139- self._cr.rollback()
140- self._cr.close()
141+ if exc_type is None and not self._failed:
142+ self._cr.commit()
143+ self._cr.close()
144 # just to be sure no one tries to re-use the request
145 self.disable_db = True
146 self.uid = None
147@@ -294,7 +286,7 @@
148 def checked_call(___dbname, *a, **kw):
149 # The decorator can call us more than once if there is an database error. In this
150 # case, the request cursor is unusable. Rollback transaction to create a new one.
151- if self._cr and not openerp.tools.config['test_enable']:
152+ if self._cr:
153 self._cr.rollback()
154 return self.endpoint(*a, **kw)
155
156
157=== modified file 'openerp/modules/registry.py'
158--- openerp/modules/registry.py 2014-02-16 21:22:22 +0000
159+++ openerp/modules/registry.py 2014-04-09 13:52:38 +0000
160@@ -58,7 +58,10 @@
161 self._init_modules = set()
162
163 self.db_name = db_name
164- self.db = openerp.sql_db.db_connect(db_name)
165+ self._db = openerp.sql_db.db_connect(db_name)
166+
167+ # special cursor for test mode; None means "normal" mode
168+ self.test_cr = None
169
170 # Indicates that the registry is
171 self.ready = False
172@@ -75,7 +78,7 @@
173 # Useful only in a multi-process context.
174 self._any_cache_cleared = False
175
176- cr = self.db.cursor()
177+ cr = self.cursor()
178 has_unaccent = openerp.modules.db.has_unaccent(cr)
179 if openerp.tools.config['unaccent'] and not has_unaccent:
180 _logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
181@@ -102,6 +105,10 @@
182 """ Return the model with the given name or raise KeyError if it doesn't exist."""
183 return self.models[model_name]
184
185+ def __call__(self, model_name):
186+ """ Same as ``self[model_name]``. """
187+ return self.models[model_name]
188+
189 def do_parent_store(self, cr):
190 for o in self._init_parent:
191 self.get(o)._parent_store_compute(cr)
192@@ -183,27 +190,38 @@
193 r, c)
194 return r, c
195
196- @contextmanager
197- def cursor(self, auto_commit=True):
198- cr = self.db.cursor()
199- try:
200- yield cr
201- if auto_commit:
202- cr.commit()
203- finally:
204- cr.close()
205-
206-class TestRLock(object):
207- def __init__(self):
208- self._lock = threading.RLock()
209+ def enter_test_mode(self):
210+ """ Enter the 'test' mode, where one cursor serves several requests. """
211+ assert self.test_cr is None
212+ self.test_cr = self._db.test_cursor()
213+ RegistryManager.enter_test_mode()
214+
215+ def leave_test_mode(self):
216+ """ Leave the test mode. """
217+ assert self.test_cr is not None
218+ self.test_cr.close(force=True) # close the cursor for real
219+ self.test_cr = None
220+ RegistryManager.leave_test_mode()
221+
222+ def cursor(self):
223+ """ Return a new cursor for the database. The cursor itself may be used
224+ as a context manager to commit/rollback and close automatically.
225+ """
226+ if self.test_cr is not None:
227+ # While in test mode, we use one special cursor across requests. The
228+ # test cursor uses a reentrant lock to serialize accesses. The lock
229+ # is granted here by cursor(), and automatically released by the
230+ # cursor itself in its method close().
231+ self.test_cr.acquire()
232+ return self.test_cr
233+ return self._db.cursor()
234+
235+class DummyRLock(object):
236+ """ Dummy reentrant lock, to be used while running rpc and js tests """
237 def acquire(self):
238- if openerp.tools.config['test_enable']:
239- return
240- return self._lock.acquire()
241+ pass
242 def release(self):
243- if openerp.tools.config['test_enable']:
244- return
245- return self._lock.release()
246+ pass
247 def __enter__(self):
248 self.acquire()
249 def __exit__(self, type, value, traceback):
250@@ -219,12 +237,30 @@
251 # Mapping between db name and model registry.
252 # Accessed through the methods below.
253 registries = {}
254- registries_lock = TestRLock()
255+ _lock = threading.RLock()
256+ _saved_lock = None
257+
258+ @classmethod
259+ def lock(cls):
260+ """ Return the current registry lock. """
261+ return cls._lock
262+
263+ @classmethod
264+ def enter_test_mode(cls):
265+ """ Enter the 'test' mode, where the registry is no longer locked. """
266+ assert cls._saved_lock is None
267+ cls._lock, cls._saved_lock = DummyRLock(), cls._lock
268+
269+ @classmethod
270+ def leave_test_mode(cls):
271+ """ Leave the 'test' mode. """
272+ assert cls._saved_lock is not None
273+ cls._lock, cls._saved_lock = cls._saved_lock, None
274
275 @classmethod
276 def get(cls, db_name, force_demo=False, status=None, update_module=False):
277 """ Return a registry for a given database name."""
278- with cls.registries_lock:
279+ with cls.lock():
280 try:
281 return cls.registries[db_name]
282 except KeyError:
283@@ -244,7 +280,7 @@
284
285 """
286 import openerp.modules
287- with cls.registries_lock:
288+ with cls.lock():
289 registry = Registry(db_name)
290
291 # Initializing a registry will call general code which will in turn
292@@ -259,7 +295,7 @@
293 registry.base_registry_signaling_sequence = seq_registry
294 registry.base_cache_signaling_sequence = seq_cache
295 # This should be a method on Registry
296- openerp.modules.load_modules(registry.db, force_demo, status, update_module)
297+ openerp.modules.load_modules(registry._db, force_demo, status, update_module)
298 except Exception:
299 del cls.registries[db_name]
300 raise
301@@ -269,7 +305,7 @@
302 # Yeah, crazy.
303 registry = cls.registries[db_name]
304
305- cr = registry.db.cursor()
306+ cr = registry.cursor()
307 try:
308 registry.do_parent_store(cr)
309 cr.commit()
310@@ -286,7 +322,7 @@
311 @classmethod
312 def delete(cls, db_name):
313 """Delete the registry linked to a given database. """
314- with cls.registries_lock:
315+ with cls.lock():
316 if db_name in cls.registries:
317 cls.registries[db_name].clear_caches()
318 del cls.registries[db_name]
319@@ -294,7 +330,7 @@
320 @classmethod
321 def delete_all(cls):
322 """Delete all the registries. """
323- with cls.registries_lock:
324+ with cls.lock():
325 for db_name in cls.registries.keys():
326 cls.delete(db_name)
327
328@@ -309,7 +345,7 @@
329 This method is given to spare you a ``RegistryManager.get(db_name)``
330 that would loads the given database if it was not already loaded.
331 """
332- with cls.registries_lock:
333+ with cls.lock():
334 if db_name in cls.registries:
335 cls.registries[db_name].clear_caches()
336
337@@ -325,7 +361,7 @@
338 changed = False
339 if openerp.multi_process and db_name in cls.registries:
340 registry = cls.get(db_name)
341- cr = registry.db.cursor()
342+ cr = registry.cursor()
343 try:
344 cr.execute("""
345 SELECT base_registry_signaling.last_value,
346@@ -371,7 +407,7 @@
347 registry = cls.get(db_name)
348 if registry.any_cache_cleared():
349 _logger.info("At least one model cache has been cleared, signaling through the database.")
350- cr = registry.db.cursor()
351+ cr = registry.cursor()
352 r = 1
353 try:
354 cr.execute("select nextval('base_cache_signaling')")
355@@ -386,7 +422,7 @@
356 if openerp.multi_process and db_name in cls.registries:
357 _logger.info("Registry changed, signaling through the database")
358 registry = cls.get(db_name)
359- cr = registry.db.cursor()
360+ cr = registry.cursor()
361 r = 1
362 try:
363 cr.execute("select nextval('base_registry_signaling')")
364
365=== modified file 'openerp/pooler.py'
366--- openerp/pooler.py 2013-03-27 14:16:53 +0000
367+++ openerp/pooler.py 2014-04-09 13:52:38 +0000
368@@ -36,7 +36,7 @@
369 assert openerp.conf.deprecation.openerp_pooler
370 _logger.warning('openerp.pooler.get_db_and_pool() is deprecated.')
371 registry = RegistryManager.get(db_name, force_demo, status, update_module)
372- return registry.db, registry
373+ return registry._db, registry
374
375
376 def restart_pool(db_name, force_demo=False, status=None, update_module=False):
377@@ -44,7 +44,7 @@
378 _logger.warning('openerp.pooler.restart_pool() is deprecated.')
379 assert openerp.conf.deprecation.openerp_pooler
380 registry = RegistryManager.new(db_name, force_demo, status, update_module)
381- return registry.db, registry
382+ return registry._db, registry
383
384 def get_db(db_name):
385 """Return a database connection. The corresponding registry is initialized."""
386
387=== modified file 'openerp/service/model.py'
388--- openerp/service/model.py 2014-02-09 00:40:05 +0000
389+++ openerp/service/model.py 2014-04-09 13:52:38 +0000
390@@ -161,21 +161,10 @@
391 def execute_kw(db, uid, obj, method, args, kw=None):
392 return execute(db, uid, obj, method, *args, **kw or {})
393
394-@contextmanager
395-def closing_cr_and_commit(cr):
396- try:
397- yield cr
398- cr.commit()
399- except Exception:
400- cr.rollback()
401- raise
402- finally:
403- cr.close()
404-
405 @check
406 def execute(db, uid, obj, method, *args, **kw):
407 threading.currentThread().dbname = db
408- with closing_cr_and_commit(openerp.registry(db).db.cursor()) as cr:
409+ with openerp.registry(db).cursor() as cr:
410 if method.startswith('_'):
411 raise except_orm('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
412 res = execute_cr(cr, uid, obj, method, *args, **kw)
413@@ -190,7 +179,7 @@
414
415 @check
416 def exec_workflow(db, uid, obj, signal, *args):
417- with closing_cr_and_commit(openerp.registry(db).db.cursor()) as cr:
418+ with openerp.registry(db).cursor() as cr:
419 return exec_workflow_cr(cr, uid, obj, signal, *args)
420
421 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
422
423=== modified file 'openerp/service/report.py'
424--- openerp/service/report.py 2013-03-27 16:40:45 +0000
425+++ openerp/service/report.py 2014-04-09 13:52:38 +0000
426@@ -49,7 +49,7 @@
427
428 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}
429
430- cr = openerp.registry(db).db.cursor()
431+ cr = openerp.registry(db).cursor()
432 try:
433 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)
434 if not result:
435@@ -87,7 +87,7 @@
436 self_reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None}
437
438 def go(id, uid, ids, datas, context):
439- cr = openerp.registry(db).db.cursor()
440+ cr = openerp.registry(db).cursor()
441 try:
442 result, format = openerp.report.render_report(cr, uid, ids, object, datas, context)
443 if not result:
444
445=== modified file 'openerp/sql_db.py'
446--- openerp/sql_db.py 2014-03-17 12:43:43 +0000
447+++ openerp/sql_db.py 2014-04-09 13:52:38 +0000
448@@ -347,6 +347,23 @@
449 """
450 return self._cnx.rollback()
451
452+ def __enter__(self):
453+ """ Using the cursor as a contextmanager automatically commits and
454+ closes it::
455+
456+ with cr:
457+ cr.execute(...)
458+
459+ # cr is committed if no failure occurred
460+ # cr is closed in any case
461+ """
462+ return self
463+
464+ def __exit__(self, exc_type, exc_value, traceback):
465+ if exc_type is None:
466+ self.commit()
467+ self.close()
468+
469 @contextmanager
470 @check
471 def savepoint(self):
472@@ -364,6 +381,41 @@
473 def __getattr__(self, name):
474 return getattr(self._obj, name)
475
476+class TestCursor(Cursor):
477+ """ A cursor to be used for tests. It keeps the transaction open across
478+ several requests, and simulates committing, rolling back, and closing.
479+ """
480+ def __init__(self, *args, **kwargs):
481+ # in order to simulate commit and rollback, the cursor maintains a
482+ # savepoint at its last commit
483+ super(TestCursor, self).__init__(*args, **kwargs)
484+ self.execute("SAVEPOINT test_cursor")
485+ self._lock = threading.RLock()
486+
487+ def acquire(self):
488+ self._lock.acquire()
489+
490+ def release(self):
491+ self._lock.release()
492+
493+ def close(self, force=False):
494+ if force:
495+ super(TestCursor, self).close()
496+ elif not self._closed:
497+ self.rollback() # for stuff that has not been committed
498+ self.release()
499+
500+ def autocommit(self, on):
501+ _logger.debug("TestCursor.autocommit(%r) does nothing", on)
502+
503+ def commit(self):
504+ self.execute("RELEASE SAVEPOINT test_cursor")
505+ self.execute("SAVEPOINT test_cursor")
506+
507+ def rollback(self):
508+ self.execute("ROLLBACK TO SAVEPOINT test_cursor")
509+ self.execute("SAVEPOINT test_cursor")
510+
511 class PsycoConnection(psycopg2.extensions.connection):
512 pass
513
514@@ -491,6 +543,11 @@
515 _logger.debug('create %scursor to %r', cursor_type, self.dbname)
516 return Cursor(self._pool, self.dbname, serialized=serialized)
517
518+ def test_cursor(self, serialized=True):
519+ cursor_type = serialized and 'serialized ' or ''
520+ _logger.debug('create test %scursor to %r', cursor_type, self.dbname)
521+ return TestCursor(self._pool, self.dbname, serialized=serialized)
522+
523 # serialized_cursor is deprecated - cursors are serialized by default
524 serialized_cursor = cursor
525
526
527=== modified file 'openerp/tests/common.py'
528--- openerp/tests/common.py 2014-04-03 13:41:09 +0000
529+++ openerp/tests/common.py 2014-04-09 13:52:38 +0000
530@@ -20,6 +20,7 @@
531 import werkzeug
532
533 import openerp
534+from openerp.modules.registry import RegistryManager
535
536 _logger = logging.getLogger(__name__)
537
538@@ -37,25 +38,6 @@
539 # Useless constant, tests are aware of the content of demo data
540 ADMIN_USER_ID = openerp.SUPERUSER_ID
541
542-# Magic session_id, unfortunately we have to serialize access to the cursors to
543-# serialize requests. We first tried to duplicate the database for each tests
544-# but this proved too slow. Any idea to improve this is welcome.
545-HTTP_SESSION = {}
546-
547-def acquire_test_cursor(session_id):
548- if openerp.tools.config['test_enable']:
549- cr = HTTP_SESSION.get(session_id)
550- if cr:
551- cr._test_lock.acquire()
552- return cr
553-
554-def release_test_cursor(cr):
555- if openerp.tools.config['test_enable']:
556- if hasattr(cr, '_test_lock'):
557- cr._test_lock.release()
558- return True
559- return False
560-
561 def at_install(flag):
562 """ Sets the at-install state of a test, the flag is a boolean specifying
563 whether the test should (``True``) or should not (``False``) run during
564@@ -67,6 +49,7 @@
565 obj.at_install = flag
566 return obj
567 return decorator
568+
569 def post_install(flag):
570 """ Sets the post-install state of a test. The flag is a boolean
571 specifying whether the test should or should not run after a set of
572@@ -83,18 +66,13 @@
573 """
574 Subclass of TestCase for common OpenERP-specific code.
575
576- This class is abstract and expects self.cr and self.uid to be initialized by subclasses.
577+ This class is abstract and expects self.registry, self.cr and self.uid to be
578+ initialized by subclasses.
579 """
580
581- @classmethod
582 def cursor(self):
583- return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
584-
585- @classmethod
586- def registry(self, model):
587- return openerp.modules.registry.RegistryManager.get(DB)[model]
588-
589- @classmethod
590+ return self.registry.cursor()
591+
592 def ref(self, xid):
593 """ Returns database ID corresponding to a given identifier.
594
595@@ -106,7 +84,6 @@
596 _, id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, module, xid)
597 return id
598
599- @classmethod
600 def browse_ref(self, xid):
601 """ Returns a browsable record for the given identifier.
602
603@@ -125,10 +102,9 @@
604 """
605
606 def setUp(self):
607- # Store cr and uid in class variables, to allow ref() and browse_ref to be BaseCase @classmethods
608- # and still access them
609- TransactionCase.cr = self.cursor()
610- TransactionCase.uid = openerp.SUPERUSER_ID
611+ self.registry = RegistryManager.get(DB)
612+ self.cr = self.cursor()
613+ self.uid = openerp.SUPERUSER_ID
614
615 def tearDown(self):
616 self.cr.rollback()
617@@ -143,7 +119,8 @@
618
619 @classmethod
620 def setUpClass(cls):
621- cls.cr = cls.cursor()
622+ cls.registry = RegistryManager.get(DB)
623+ cls.cr = cls.registry.cursor()
624 cls.uid = openerp.SUPERUSER_ID
625
626 @classmethod
627@@ -166,16 +143,15 @@
628
629 def setUp(self):
630 super(HttpCase, self).setUp()
631+ self.registry.enter_test_mode()
632 # setup a magic session_id that will be rollbacked
633 self.session = openerp.http.root.session_store.new()
634 self.session_id = self.session.sid
635 self.session.db = DB
636 openerp.http.root.session_store.save(self.session)
637- self.cr._test_lock = threading.RLock()
638- HTTP_SESSION[self.session_id] = self.cr
639
640 def tearDown(self):
641- del HTTP_SESSION[self.session_id]
642+ self.registry.leave_test_mode()
643 super(HttpCase, self).tearDown()
644
645 def url_open(self, url, data=None, timeout=10):
646
647=== modified file 'openerp/tools/mail.py'
648--- openerp/tools/mail.py 2014-02-19 14:06:17 +0000
649+++ openerp/tools/mail.py 2014-04-09 13:52:38 +0000
650@@ -625,7 +625,7 @@
651 if not cr:
652 db_name = getattr(threading.currentThread(), 'dbname', None)
653 if db_name:
654- local_cr = cr = openerp.registry(db_name).db.cursor()
655+ local_cr = cr = openerp.registry(db_name).cursor()
656 else:
657 raise Exception("No database cursor found, please pass one explicitly")
658
659
660=== modified file 'openerpcommand/module.py'
661--- openerpcommand/module.py 2013-01-11 13:46:57 +0000
662+++ openerpcommand/module.py 2014-04-09 13:52:38 +0000
663@@ -33,12 +33,9 @@
664
665 xs = []
666 ir_module_module = registry.get('ir.module.module')
667- cr = registry.db.cursor() # TODO context manager
668- try:
669+ with registry.cursor() as cr:
670 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [], {})
671 xs = ir_module_module.read(cr, openerp.SUPERUSER_ID, ids, [], {})
672- finally:
673- cr.close()
674
675 if xs:
676 print "Modules (database `%s`):" % (args.database,)
677
678=== modified file 'openerpcommand/read.py'
679--- openerpcommand/read.py 2013-03-29 14:07:23 +0000
680+++ openerpcommand/read.py 2014-04-09 13:52:38 +0000
681@@ -20,15 +20,12 @@
682 registry = openerp.modules.registry.RegistryManager.get(
683 args.database, update_module=False)
684 model = registry[args.model]
685- cr = registry.db.cursor() # TODO context manager
686 field_names = [args.field] if args.field else []
687 if args.short:
688 # ignore --field
689 field_names = ['name']
690- try:
691+ with registry.cursor() as cr:
692 xs = model.read(cr, 1, args.id, field_names, {})
693- finally:
694- cr.close()
695
696 if xs:
697 print "Records (model `%s`, database `%s`):" % (args.model, args.database)
698
699=== modified file 'openerpcommand/uninstall.py'
700--- openerpcommand/uninstall.py 2013-02-09 06:02:46 +0000
701+++ openerpcommand/uninstall.py 2014-04-09 13:52:38 +0000
702@@ -43,15 +43,12 @@
703 args.database, update_module=False)
704
705 ir_module_module = registry.get('ir.module.module')
706- cr = registry.db.cursor() # TODO context manager
707- try:
708+ with registry.cursor() as cr:
709 ids = ir_module_module.search(cr, openerp.SUPERUSER_ID, [('name', 'in', args.module), ('state', '=', 'installed')], {})
710 if len(ids) == len(args.module):
711 ir_module_module.button_immediate_uninstall(cr, openerp.SUPERUSER_ID, ids, {})
712 else:
713 print "At least one module not found (database `%s`)." % (args.database,)
714- finally:
715- cr.close()
716
717 def add_parser(subparsers):
718 parser = subparsers.add_parser('uninstall',