Merge lp:~openerp-dev/openobject-server/trunk-test_cursor-rco into lp:openobject-server
- trunk-test_cursor-rco
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+214925@code.launchpad.net |
Commit message
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)
Christophe Simonis (OpenERP) (kangol) wrote : | # |
- 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
Raphael Collet (OpenERP) (rco-openerp) wrote : | # |
openerp/
openerp/sql_db.py: removed overriding of execute()
In Registry, I renamed registry.db into registry._db, and rewrote every call to registry.
- 5171. By Raphael Collet (OpenERP)
-
[FIX] registry: stupid typo
Preview Diff
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', |
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)