Merge lp:~adam-collard/storm/kill-django-storm into lp:storm
- kill-django-storm
- Merge into trunk
Proposed by
Adam Collard
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Benji York | ||||
Approved revision: | 482 | ||||
Merged at revision: | 480 | ||||
Proposed branch: | lp:~adam-collard/storm/kill-django-storm | ||||
Merge into: | lp:storm | ||||
Prerequisite: | lp:~adam-collard/storm/sqlobject2-compatibility | ||||
Diff against target: |
1048 lines (+12/-958) 11 files modified
NEWS (+11/-0) README (+1/-1) dev/ubuntu-deps (+0/-1) setup.py (+0/-1) storm/django/__init__.py (+0/-20) storm/django/backend/base.py (+0/-136) storm/django/middleware.py (+0/-86) storm/django/stores.py (+0/-61) tests/django/backend.py (+0/-323) tests/django/middleware.py (+0/-220) tests/django/stores.py (+0/-109) |
||||
To merge this branch: | bzr merge lp:~adam-collard/storm/kill-django-storm | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Collard (community) | Abstain | ||
Benji York (community) | Approve | ||
Review via email: mp+287630@code.launchpad.net |
Commit message
Description of the change
Remove Django support in Storm.
The Django support has long since bit-rotted in Storm, and searching on http://
With this branch merged, tests should pass on Xenial.
To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) : | # |
review:
Abstain
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' |
2 | --- NEWS 2014-12-28 11:51:53 +0000 |
3 | +++ NEWS 2016-03-01 13:17:28 +0000 |
4 | @@ -8,6 +8,17 @@ |
5 | package, which can apply patches "in parallel" against a set of stores. See |
6 | the module docstring for more information. |
7 | |
8 | +Bug fixes |
9 | +--------- |
10 | + |
11 | +- Fixed SQLObject tests to work with SQLObject 2.x by using Unicode strings for |
12 | + LIKE operations. |
13 | + |
14 | +API changes |
15 | +----------- |
16 | + |
17 | +- Removed Django support, storm.django is no more. |
18 | + |
19 | 0.20 (2013-06-28) |
20 | ================= |
21 | |
22 | |
23 | === modified file 'README' |
24 | --- README 2012-03-06 10:28:06 +0000 |
25 | +++ README 2016-03-01 13:17:28 +0000 |
26 | @@ -127,7 +127,7 @@ |
27 | apt-get: |
28 | |
29 | $ apt-get install \ |
30 | - python-django python-fixtures python-psycopg2 \ |
31 | + python-fixtures python-psycopg2 \ |
32 | python-testresources python-transaction python-twisted \ |
33 | python-zope.component python-zope.security |
34 | |
35 | |
36 | === modified file 'dev/ubuntu-deps' |
37 | --- dev/ubuntu-deps 2014-05-22 16:45:50 +0000 |
38 | +++ dev/ubuntu-deps 2016-03-01 13:17:28 +0000 |
39 | @@ -11,7 +11,6 @@ |
40 | postgresql \ |
41 | pgbouncer \ |
42 | build-essential \ |
43 | - python-django \ |
44 | python-fixtures \ |
45 | python-psycopg2 \ |
46 | python-testresources \ |
47 | |
48 | === modified file 'setup.py' |
49 | --- setup.py 2011-10-28 14:44:04 +0000 |
50 | +++ setup.py 2016-03-01 13:17:28 +0000 |
51 | @@ -53,7 +53,6 @@ |
52 | test_suite = "tests.find_tests", |
53 | tests_require=[ |
54 | # Versions based on Lucid, where packaged. |
55 | - "django >= 1.1.1", |
56 | "fixtures >= 0.3.5", |
57 | # pgbouncer (the Python module) is not yet packaged in Ubuntu. |
58 | "pgbouncer >= 0.0.7", |
59 | |
60 | === removed file 'storm/django/__init__.py' |
61 | --- storm/django/__init__.py 2008-07-30 08:45:40 +0000 |
62 | +++ storm/django/__init__.py 1970-01-01 00:00:00 +0000 |
63 | @@ -1,20 +0,0 @@ |
64 | -# |
65 | -# Copyright (c) 2008 Canonical |
66 | -# |
67 | -# Written by James Henstridge <jamesh@canonical.com> |
68 | -# |
69 | -# This file is part of Storm Object Relational Mapper. |
70 | -# |
71 | -# Storm is free software; you can redistribute it and/or modify |
72 | -# it under the terms of the GNU Lesser General Public License as |
73 | -# published by the Free Software Foundation; either version 2.1 of |
74 | -# the License, or (at your option) any later version. |
75 | -# |
76 | -# Storm is distributed in the hope that it will be useful, |
77 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
78 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
79 | -# GNU Lesser General Public License for more details. |
80 | -# |
81 | -# You should have received a copy of the GNU Lesser General Public License |
82 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
83 | -# |
84 | |
85 | === removed file 'storm/django/backend/base.py' |
86 | --- storm/django/backend/base.py 2012-03-05 20:04:10 +0000 |
87 | +++ storm/django/backend/base.py 1970-01-01 00:00:00 +0000 |
88 | @@ -1,136 +0,0 @@ |
89 | -__metaclass__ = type |
90 | - |
91 | -__all__ = [ |
92 | - 'DatabaseWrapper', 'DatabaseError', 'IntegrityError', |
93 | - ] |
94 | - |
95 | -from django.conf import settings |
96 | -import transaction |
97 | - |
98 | -from storm.django.stores import get_store, get_store_uri |
99 | -from storm.exceptions import DatabaseError, IntegrityError |
100 | - |
101 | - |
102 | -class StormDatabaseWrapperMixin(object): |
103 | - |
104 | - _store = None |
105 | - |
106 | - def _get_connection(self): |
107 | - if self._store is None: |
108 | - self._store = get_store(settings.DATABASE_NAME) |
109 | - # Make sure that the store is registered with the transaction |
110 | - # manager: we don't know what the connection will be used for. |
111 | - self._store._event.emit("register-transaction") |
112 | - self._store._connection._ensure_connected() |
113 | - return self._store._connection._raw_connection |
114 | - |
115 | - def _set_connection(self, connection): |
116 | - # Ignore attempts to set the connection. |
117 | - pass |
118 | - |
119 | - connection = property(_get_connection, _set_connection) |
120 | - |
121 | - def _valid_connection(self): |
122 | - # Storm handles the connection liveness checks. |
123 | - return True |
124 | - |
125 | - def _cursor(self, *args): |
126 | - cursor = super(StormDatabaseWrapperMixin, self)._cursor(*args) |
127 | - return StormCursorWrapper(self._store, cursor) |
128 | - |
129 | - def _commit(self): |
130 | - #print "commit" |
131 | - try: |
132 | - transaction.commit() |
133 | - except Exception: |
134 | - transaction.abort() |
135 | - raise |
136 | - |
137 | - def _rollback(self): |
138 | - #print "rollback" |
139 | - transaction.abort() |
140 | - |
141 | - def close(self): |
142 | - # As we are borrowing Storm's connection, we shouldn't close |
143 | - # it behind Storm's back. |
144 | - self._store = None |
145 | - |
146 | - |
147 | -class StormCursorWrapper(object): |
148 | - """A cursor wrapper that checks for disconnection errors.""" |
149 | - |
150 | - def __init__(self, store, cursor): |
151 | - self._connection = store._connection |
152 | - self._cursor = cursor |
153 | - |
154 | - def _check_disconnect(self, *args, **kwargs): |
155 | - from django.db import DatabaseError as DjangoDatabaseError |
156 | - kwargs['extra_disconnection_errors'] = DjangoDatabaseError |
157 | - return self._connection._check_disconnect(*args, **kwargs) |
158 | - |
159 | - def execute(self, statement, *args): |
160 | - """Execute an SQL statement.""" |
161 | - return self._check_disconnect(self._cursor.execute, statement, *args) |
162 | - |
163 | - def fetchone(self): |
164 | - """Fetch one row from the result.""" |
165 | - return self._check_disconnect(self._cursor.fetchone) |
166 | - |
167 | - def fetchall(self): |
168 | - """Fetch all rows from the result.""" |
169 | - return self._check_disconnect(self._cursor.fetchall) |
170 | - |
171 | - def fetchmany(self, *args): |
172 | - """Fetch multiple rows from the result.""" |
173 | - return self._check_disconnect(self._cursor.fetchmany, *args) |
174 | - |
175 | - @property |
176 | - def description(self): |
177 | - """Fetch the description of the result.""" |
178 | - return self._check_disconnect(getattr, self._cursor, "description") |
179 | - |
180 | - @property |
181 | - def rowcount(self): |
182 | - """Fetch the number of rows in the result.""" |
183 | - return self._check_disconnect(getattr, self._cursor, "rowcount") |
184 | - |
185 | - @property |
186 | - def query(self): |
187 | - """Fetch the last executed query.""" |
188 | - return self._check_disconnect(getattr, self._cursor, "query") |
189 | - |
190 | - |
191 | -PostgresStormDatabaseWrapper = None |
192 | -MySQLStormDatabaseWrapper = None |
193 | - |
194 | - |
195 | -def DatabaseWrapper(*args, **kwargs): |
196 | - store_uri = get_store_uri(settings.DATABASE_NAME) |
197 | - |
198 | - # Create a DatabaseWrapper class that uses an underlying Storm |
199 | - # connection. We don't support sqlite here because Django expects |
200 | - # a bunch of special setup on the connection that Storm doesn't |
201 | - # do. |
202 | - if store_uri.startswith('postgres:'): |
203 | - global PostgresStormDatabaseWrapper |
204 | - if PostgresStormDatabaseWrapper is None: |
205 | - from django.db.backends.postgresql_psycopg2.base import ( |
206 | - DatabaseWrapper as PostgresDatabaseWrapper) |
207 | - class PostgresStormDatabaseWrapper(StormDatabaseWrapperMixin, |
208 | - PostgresDatabaseWrapper): |
209 | - pass |
210 | - DatabaseWrapper = PostgresStormDatabaseWrapper |
211 | - elif store_uri.startswith('mysql:'): |
212 | - global MySQLStormDatabaseWrapper |
213 | - if MySQLStormDatabaseWrapper is None: |
214 | - from django.db.backends.mysql.base import ( |
215 | - DatabaseWrapper as MySQLDatabaseWrapper) |
216 | - class MySQLStormDatabaseWrapper(StormDatabaseWrapperMixin, |
217 | - MySQLDatabaseWrapper): |
218 | - pass |
219 | - DatabaseWrapper = MySQLStormDatabaseWrapper |
220 | - else: |
221 | - assert False, ( |
222 | - "Unsupported database backend: %s" % store_uri) |
223 | - |
224 | - return DatabaseWrapper(*args, **kwargs) |
225 | |
226 | === removed file 'storm/django/middleware.py' |
227 | --- storm/django/middleware.py 2011-11-14 21:50:14 +0000 |
228 | +++ storm/django/middleware.py 1970-01-01 00:00:00 +0000 |
229 | @@ -1,86 +0,0 @@ |
230 | -# |
231 | -# Copyright (c) 2008 Canonical |
232 | -# |
233 | -# Written by James Henstridge <jamesh@canonical.com> |
234 | -# |
235 | -# This file is part of Storm Object Relational Mapper. |
236 | -# |
237 | -# Storm is free software; you can redistribute it and/or modify |
238 | -# it under the terms of the GNU Lesser General Public License as |
239 | -# published by the Free Software Foundation; either version 2.1 of |
240 | -# the License, or (at your option) any later version. |
241 | -# |
242 | -# Storm is distributed in the hope that it will be useful, |
243 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
244 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
245 | -# GNU Lesser General Public License for more details. |
246 | -# |
247 | -# You should have received a copy of the GNU Lesser General Public License |
248 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
249 | -# |
250 | - |
251 | -"""Django middleware support for the Zope transaction manager. |
252 | - |
253 | -Adding storm.django.middleware.ZopeTransactionMiddleware to |
254 | -L{MIDDLEWARE_CLASSES} in the application's settings module will cause a |
255 | -Zope transaction to be run for each request. |
256 | -""" |
257 | - |
258 | -__all__ = ['ZopeTransactionMiddleware'] |
259 | - |
260 | - |
261 | -from django.conf import settings |
262 | - |
263 | -import transaction |
264 | - |
265 | - |
266 | -class ZopeTransactionMiddleware(object): |
267 | - """Zope Transaction middleware for Django. |
268 | - |
269 | - If this is enabled, a Zope transaction will be run to cover each |
270 | - request. |
271 | - """ |
272 | - def __init__(self): |
273 | - self.commit_safe_methods = getattr( |
274 | - settings, 'STORM_COMMIT_SAFE_METHODS', True) |
275 | - |
276 | - def process_request(self, request): |
277 | - """Begin a transaction on request start..""" |
278 | - from django.db import transaction as django_transaction |
279 | - django_transaction.enter_transaction_management() |
280 | - django_transaction.managed(True) |
281 | - transaction.begin() |
282 | - |
283 | - def process_exception(self, request, exception): |
284 | - """Abort the transaction on errors.""" |
285 | - from django.db import transaction as django_transaction |
286 | - transaction.abort() |
287 | - django_transaction.set_clean() |
288 | - django_transaction.leave_transaction_management() |
289 | - |
290 | - def process_response(self, request, response): |
291 | - """Commit or abort the transaction after processing the response. |
292 | - |
293 | - On successful completion of the request, the transaction will |
294 | - be committed. |
295 | - |
296 | - As an exception to this, if the L{STORM_COMMIT_SAFE_METHODS} |
297 | - setting is False, and the request used either of the GET and |
298 | - HEAD methods, the transaction will be aborted. |
299 | - """ |
300 | - from django.db import transaction as django_transaction |
301 | - # If process_exception() has been called, then we'll no longer |
302 | - # be in managed transaction mode. |
303 | - if django_transaction.is_managed(): |
304 | - if self.commit_safe_methods or ( |
305 | - request.method not in ['HEAD', 'GET']): |
306 | - try: |
307 | - transaction.commit() |
308 | - except Exception: |
309 | - transaction.abort() |
310 | - raise |
311 | - else: |
312 | - transaction.abort() |
313 | - django_transaction.set_clean() |
314 | - django_transaction.leave_transaction_management() |
315 | - return response |
316 | |
317 | === removed file 'storm/django/stores.py' |
318 | --- storm/django/stores.py 2009-08-05 06:23:43 +0000 |
319 | +++ storm/django/stores.py 1970-01-01 00:00:00 +0000 |
320 | @@ -1,61 +0,0 @@ |
321 | -# |
322 | -# Copyright (c) 2008 Canonical |
323 | -# |
324 | -# Written by James Henstridge <jamesh@canonical.com> |
325 | -# |
326 | -# This file is part of Storm Object Relational Mapper. |
327 | -# |
328 | -# Storm is free software; you can redistribute it and/or modify |
329 | -# it under the terms of the GNU Lesser General Public License as |
330 | -# published by the Free Software Foundation; either version 2.1 of |
331 | -# the License, or (at your option) any later version. |
332 | -# |
333 | -# Storm is distributed in the hope that it will be useful, |
334 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
335 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
336 | -# GNU Lesser General Public License for more details. |
337 | -# |
338 | -# You should have received a copy of the GNU Lesser General Public License |
339 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
340 | -# |
341 | - |
342 | -"""Support for configuration and management of Storm stores in a Django app.""" |
343 | - |
344 | -__all__ = ["ensure_stores_configured", "get_store", "get_store_uri"] |
345 | - |
346 | - |
347 | -from storm.zope.zstorm import global_zstorm |
348 | - |
349 | -from django.conf import settings |
350 | -from django.core.exceptions import ImproperlyConfigured |
351 | - |
352 | - |
353 | -def configure_stores(settings): |
354 | - if not hasattr(settings, "STORM_STORES"): |
355 | - raise ImproperlyConfigured( |
356 | - "You need to specify STORM_STORES in your Django settings file.") |
357 | - |
358 | - for name, uri in settings.STORM_STORES.iteritems(): |
359 | - global_zstorm.set_default_uri(name, uri) |
360 | - |
361 | - |
362 | -have_configured_stores = False |
363 | - |
364 | - |
365 | -def ensure_stores_configured(): |
366 | - global have_configured_stores |
367 | - if not have_configured_stores: |
368 | - configure_stores(settings) |
369 | - have_configured_stores = True |
370 | - |
371 | - |
372 | -def get_store(name): |
373 | - # Make sure that stores have been configured. |
374 | - ensure_stores_configured() |
375 | - |
376 | - return global_zstorm.get(name) |
377 | - |
378 | - |
379 | -def get_store_uri(name): |
380 | - ensure_stores_configured() |
381 | - return global_zstorm.get_default_uris()[name] |
382 | |
383 | === removed file 'tests/django/backend.py' |
384 | --- tests/django/backend.py 2015-01-25 09:29:29 +0000 |
385 | +++ tests/django/backend.py 1970-01-01 00:00:00 +0000 |
386 | @@ -1,323 +0,0 @@ |
387 | - |
388 | -# Copyright (c) 2008 Canonical |
389 | -# |
390 | -# Written by James Henstridge <jamesh@canonical.com> |
391 | -# |
392 | -# This file is part of Storm Object Relational Mapper. |
393 | -# |
394 | -# Storm is free software; you can redistribute it and/or modify |
395 | -# it under the terms of the GNU Lesser General Public License as |
396 | -# published by the Free Software Foundation; either version 2.1 of |
397 | -# the License, or (at your option) any later version. |
398 | -# |
399 | -# Storm is distributed in the hope that it will be useful, |
400 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
401 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
402 | -# GNU Lesser General Public License for more details. |
403 | -# |
404 | -# You should have received a copy of the GNU Lesser General Public License |
405 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
406 | -# |
407 | - |
408 | -import os |
409 | - |
410 | -try: |
411 | - import django |
412 | - import transaction |
413 | -except ImportError: |
414 | - have_django_and_transaction = False |
415 | -else: |
416 | - have_django_and_transaction = True |
417 | - from django.conf import settings |
418 | - from storm.django import stores |
419 | - from storm.zope.zstorm import global_zstorm, StoreDataManager |
420 | - |
421 | -import storm.database |
422 | -from storm.exceptions import DisconnectionError |
423 | - |
424 | -from tests.helper import TestHelper |
425 | -from tests.databases.base import DatabaseDisconnectionMixin |
426 | - |
427 | - |
428 | -def make_wrapper(): |
429 | - from storm.django.backend import base |
430 | - if django.VERSION >= (1, 1): |
431 | - wrapper = base.DatabaseWrapper({ |
432 | - 'DATABASE_NAME': settings.DATABASE_NAME, |
433 | - 'TIME_ZONE': settings.TIME_ZONE, |
434 | - 'OPTIONS': {}, |
435 | - }) |
436 | - else: |
437 | - wrapper = base.DatabaseWrapper(**settings.DATABASE_OPTIONS) |
438 | - return wrapper |
439 | - |
440 | - |
441 | -class DjangoBackendTests(object): |
442 | - |
443 | - def is_supported(self): |
444 | - return have_django_and_transaction and self.get_store_uri() is not None |
445 | - |
446 | - def setUp(self): |
447 | - super(DjangoBackendTests, self).setUp() |
448 | - settings.configure(STORM_STORES={}) |
449 | - settings.MIDDLEWARE_CLASSES += ( |
450 | - "storm.django.middleware.ZopeTransactionMiddleware",) |
451 | - settings.DATABASE_ENGINE = "storm.django.backend" |
452 | - settings.DATABASE_NAME = "django" |
453 | - settings.STORM_STORES["django"] = self.get_store_uri() |
454 | - stores.have_configured_stores = False |
455 | - self.create_tables() |
456 | - |
457 | - def tearDown(self): |
458 | - transaction.abort() |
459 | - self.drop_tables() |
460 | - if django.VERSION >= (1, 1): |
461 | - if django.VERSION >= (1, 6): |
462 | - from django.utils.functional import empty |
463 | - else: |
464 | - empty = None |
465 | - settings._wrapped = empty |
466 | - else: |
467 | - settings._target = None |
468 | - global_zstorm._reset() |
469 | - stores.have_configured_stores = False |
470 | - transaction.manager.free(transaction.get()) |
471 | - super(DjangoBackendTests, self).tearDown() |
472 | - |
473 | - def get_store_uri(self): |
474 | - raise NotImplementedError |
475 | - |
476 | - def get_wrapper_class(self): |
477 | - raise NotImplementedError |
478 | - |
479 | - def create_tables(self): |
480 | - raise NotImplementedError |
481 | - |
482 | - def drop_tables(self): |
483 | - raise NotImplementedError |
484 | - |
485 | - def test_create_wrapper(self): |
486 | - wrapper = make_wrapper() |
487 | - self.assertTrue(isinstance(wrapper, self.get_wrapper_class())) |
488 | - |
489 | - # The wrapper uses the same database connection as the store. |
490 | - store = stores.get_store("django") |
491 | - self.assertEqual(store._connection._raw_connection, wrapper.connection) |
492 | - |
493 | - def _isInTransaction(self, store): |
494 | - """Check if a Store is part of the current transaction.""" |
495 | - for dm in transaction.get()._resources: |
496 | - if isinstance(dm, StoreDataManager) and dm._store is store: |
497 | - return True |
498 | - return False |
499 | - |
500 | - def assertInTransaction(self, store): |
501 | - """Check that the given store is joined to the transaction.""" |
502 | - self.assertTrue(self._isInTransaction(store), |
503 | - "%r should be joined to the transaction" % store) |
504 | - |
505 | - def test_using_wrapper_joins_transaction(self): |
506 | - wrapper = make_wrapper() |
507 | - cursor = wrapper.cursor() |
508 | - cursor.execute("SELECT 1") |
509 | - self.assertInTransaction(stores.get_store("django")) |
510 | - |
511 | - def test_commit(self): |
512 | - wrapper = make_wrapper() |
513 | - cursor = wrapper.cursor() |
514 | - cursor.execute("INSERT INTO django_test (title) VALUES ('foo')") |
515 | - wrapper._commit() |
516 | - |
517 | - cursor = wrapper.cursor() |
518 | - cursor.execute("SELECT title FROM django_test") |
519 | - result = cursor.fetchall() |
520 | - self.assertEqual(len(result), 1) |
521 | - self.assertEqual(result[0][0], "foo") |
522 | - |
523 | - def test_rollback(self): |
524 | - wrapper = make_wrapper() |
525 | - cursor = wrapper.cursor() |
526 | - cursor.execute("INSERT INTO django_test (title) VALUES ('foo')") |
527 | - wrapper._rollback() |
528 | - |
529 | - cursor = wrapper.cursor() |
530 | - cursor.execute("SELECT title FROM django_test") |
531 | - result = cursor.fetchall() |
532 | - self.assertEqual(len(result), 0) |
533 | - |
534 | - def test_register_transaction(self): |
535 | - wrapper = make_wrapper() |
536 | - store = global_zstorm.get("django") |
537 | - # Watch for register-transaction calls. |
538 | - calls = [] |
539 | - def register_transaction(owner): |
540 | - calls.append(owner) |
541 | - store._event.hook("register-transaction", register_transaction) |
542 | - |
543 | - cursor = wrapper.cursor() |
544 | - cursor.execute("SELECT 1") |
545 | - self.assertNotEqual(calls, []) |
546 | - |
547 | - def test_abort_transaction_on_failed_commit(self): |
548 | - _transaction = transaction.get() |
549 | - resource1 = self.mocker.mock() |
550 | - self.expect(resource1.prepare).throw(AttributeError).count(0) |
551 | - self.expect(resource1.tpc_begin(_transaction)).throw(DisconnectionError) |
552 | - self.expect(resource1.abort(_transaction)).count(2) |
553 | - self.expect(resource1.sortKey()) |
554 | - self.mocker.replay() |
555 | - |
556 | - _transaction.join(resource1) |
557 | - wrapper = make_wrapper() |
558 | - cursor = wrapper.cursor() |
559 | - cursor.execute("INSERT INTO django_test (title) VALUES ('foo')") |
560 | - self.assertRaises(DisconnectionError, wrapper._commit) |
561 | - |
562 | - # Calling _get_connection on the wrapper after a failed commit should |
563 | - # work fine. Before the fix this would raise a |
564 | - # 'TransactionFailedError'. |
565 | - wrapper._get_connection() |
566 | - |
567 | - |
568 | -class DjangoBackendDisconnectionTests(DatabaseDisconnectionMixin): |
569 | - |
570 | - def is_supported(self): |
571 | - if not have_django_and_transaction: |
572 | - return False |
573 | - return DatabaseDisconnectionMixin.is_supported(self) |
574 | - |
575 | - def setUp(self): |
576 | - super(DjangoBackendDisconnectionTests, self).setUp() |
577 | - settings.configure(STORM_STORES={}) |
578 | - |
579 | - settings.DATABASE_ENGINE = "storm.django.backend" |
580 | - settings.DATABASE_NAME = "django" |
581 | - settings.STORM_STORES["django"] = str(self.proxy_uri) |
582 | - stores.have_configured_stores = False |
583 | - |
584 | - def tearDown(self): |
585 | - transaction.abort() |
586 | - if django.VERSION >= (1, 1): |
587 | - if django.VERSION >= (1, 6): |
588 | - from django.utils.functional import empty |
589 | - else: |
590 | - empty = None |
591 | - settings._wrapped = empty |
592 | - else: |
593 | - settings._target = None |
594 | - global_zstorm._reset() |
595 | - stores.have_configured_stores = False |
596 | - transaction.manager.free(transaction.get()) |
597 | - super(DjangoBackendDisconnectionTests, self).tearDown() |
598 | - |
599 | - def test_wb_disconnect(self): |
600 | - wrapper = make_wrapper() |
601 | - store = global_zstorm.get("django") |
602 | - cursor = wrapper.cursor() |
603 | - cursor.execute("SELECT 'about to reset connection'") |
604 | - wrapper._rollback() |
605 | - cursor = wrapper.cursor() |
606 | - self.proxy.restart() |
607 | - self.assertRaises(DisconnectionError, cursor.execute, "SELECT 1") |
608 | - self.assertEqual( |
609 | - store._connection._state, storm.database.STATE_DISCONNECTED) |
610 | - wrapper._rollback() |
611 | - |
612 | - self.assertEqual( |
613 | - store._connection._state, storm.database.STATE_RECONNECT) |
614 | - cursor = wrapper.cursor() |
615 | - cursor.execute("SELECT 1") |
616 | - |
617 | - def test_wb_transaction_registration(self): |
618 | - wrapper = make_wrapper() |
619 | - store = global_zstorm.get("django") |
620 | - # Watch for register-transaction calls. |
621 | - calls = [] |
622 | - def register_transaction(owner): |
623 | - calls.append(owner) |
624 | - store._event.hook("register-transaction", register_transaction) |
625 | - |
626 | - # Simulate a disconnection, and put the connection into a |
627 | - # state where it would attempt to reconnect. |
628 | - store._connection._raw_connection = None |
629 | - store._connection._state = storm.database.STATE_RECONNECT |
630 | - self.proxy.stop() |
631 | - |
632 | - self.assertRaises(DisconnectionError, wrapper.cursor) |
633 | - # The connection is in the disconnected state, and has been |
634 | - # registered with any listening transaction manager. |
635 | - self.assertNotEqual(calls, []) |
636 | - self.assertEqual( |
637 | - store._connection._state, storm.database.STATE_DISCONNECTED) |
638 | - |
639 | - wrapper._rollback() |
640 | - del calls[:] |
641 | - |
642 | - # Now reconnect: |
643 | - self.proxy.start() |
644 | - cursor = wrapper.cursor() |
645 | - cursor.execute("SELECT 1") |
646 | - # The connection is up, and has been registered with any |
647 | - # listening transaction manager. |
648 | - self.assertNotEqual(calls, []) |
649 | - self.assertEqual( |
650 | - store._connection._state, storm.database.STATE_CONNECTED) |
651 | - |
652 | - |
653 | -class PostgresDjangoBackendTests(DjangoBackendTests, TestHelper): |
654 | - |
655 | - def get_store_uri(self): |
656 | - return os.environ.get("STORM_POSTGRES_URI") |
657 | - |
658 | - def get_wrapper_class(self): |
659 | - from storm.django.backend import base |
660 | - return base.PostgresStormDatabaseWrapper |
661 | - |
662 | - def create_tables(self): |
663 | - store = stores.get_store("django") |
664 | - store.execute("CREATE TABLE django_test (" |
665 | - " id SERIAL PRIMARY KEY," |
666 | - " title TEXT)") |
667 | - transaction.commit() |
668 | - |
669 | - def drop_tables(self): |
670 | - store = stores.get_store("django") |
671 | - store.execute("DROP TABLE django_test") |
672 | - transaction.commit() |
673 | - |
674 | - |
675 | -class MySQLDjangoBackendTests(DjangoBackendTests, TestHelper): |
676 | - |
677 | - def get_store_uri(self): |
678 | - return os.environ.get("STORM_MYSQL_URI") |
679 | - |
680 | - def get_wrapper_class(self): |
681 | - from storm.django.backend import base |
682 | - return base.MySQLStormDatabaseWrapper |
683 | - |
684 | - def create_tables(self): |
685 | - store = stores.get_store("django") |
686 | - store.execute("CREATE TABLE django_test (" |
687 | - " id INT AUTO_INCREMENT PRIMARY KEY," |
688 | - " title TEXT) ENGINE=InnoDB") |
689 | - transaction.commit() |
690 | - |
691 | - def drop_tables(self): |
692 | - store = stores.get_store("django") |
693 | - store.execute("DROP TABLE django_test") |
694 | - transaction.commit() |
695 | - |
696 | -class PostgresDjangoBackendDisconnectionTests( |
697 | - DjangoBackendDisconnectionTests, TestHelper): |
698 | - |
699 | - environment_variable = "STORM_POSTGRES_URI" |
700 | - host_environment_variable = "STORM_POSTGRES_HOST_URI" |
701 | - default_port = 5432 |
702 | - |
703 | - |
704 | -class MySQLDjangoBackendDisconnectionTests( |
705 | - DjangoBackendDisconnectionTests, TestHelper): |
706 | - |
707 | - environment_variable = "STORM_MYSQL_URI" |
708 | - host_environment_variable = "STORM_MYSQL_HOST_URI" |
709 | - default_port = 3306 |
710 | |
711 | === removed file 'tests/django/middleware.py' |
712 | --- tests/django/middleware.py 2015-01-25 09:29:29 +0000 |
713 | +++ tests/django/middleware.py 1970-01-01 00:00:00 +0000 |
714 | @@ -1,220 +0,0 @@ |
715 | -# |
716 | -# Copyright (c) 2008 Canonical |
717 | -# |
718 | -# Written by James Henstridge <jamesh@canonical.com> |
719 | -# |
720 | -# This file is part of Storm Object Relational Mapper. |
721 | -# |
722 | -# Storm is free software; you can redistribute it and/or modify |
723 | -# it under the terms of the GNU Lesser General Public License as |
724 | -# published by the Free Software Foundation; either version 2.1 of |
725 | -# the License, or (at your option) any later version. |
726 | -# |
727 | -# Storm is distributed in the hope that it will be useful, |
728 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
729 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
730 | -# GNU Lesser General Public License for more details. |
731 | -# |
732 | -# You should have received a copy of the GNU Lesser General Public License |
733 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
734 | -# |
735 | - |
736 | -from storm.exceptions import DisconnectionError |
737 | - |
738 | -try: |
739 | - import django |
740 | - import transaction |
741 | -except ImportError: |
742 | - have_django_and_transaction = False |
743 | -else: |
744 | - have_django_and_transaction = True |
745 | - from django import conf |
746 | - from django.http import HttpRequest, HttpResponse |
747 | - from storm.django.middleware import ZopeTransactionMiddleware |
748 | - |
749 | -from tests.helper import TestHelper |
750 | - |
751 | - |
752 | -class TransactionMiddlewareTests(TestHelper): |
753 | - |
754 | - def is_supported(self): |
755 | - return have_django_and_transaction |
756 | - |
757 | - def setUp(self): |
758 | - super(TransactionMiddlewareTests, self).setUp() |
759 | - conf.settings.configure(STORM_COMMIT_SAFE_METHODS=False) |
760 | - from django.db import transaction as django_transaction |
761 | - self.django_transaction = django_transaction |
762 | - |
763 | - def tearDown(self): |
764 | - if django.VERSION >= (1, 1): |
765 | - if django.VERSION >= (1, 6): |
766 | - from django.utils.functional import empty |
767 | - else: |
768 | - empty = None |
769 | - conf.settings._wrapped = empty |
770 | - else: |
771 | - conf.settings._target = None |
772 | - super(TransactionMiddlewareTests, self).tearDown() |
773 | - |
774 | - def test_process_request_begins_transaction(self): |
775 | - begin = self.mocker.replace("transaction.begin") |
776 | - enter_transaction_management = self.mocker.replace( |
777 | - "django.db.transaction.enter_transaction_management") |
778 | - managed = self.mocker.replace( |
779 | - "django.db.transaction.managed") |
780 | - enter_transaction_management() |
781 | - managed(True) |
782 | - begin() |
783 | - self.mocker.replay() |
784 | - |
785 | - zope_middleware = ZopeTransactionMiddleware() |
786 | - request = HttpRequest() |
787 | - request.method = "GET" |
788 | - zope_middleware.process_request(request) |
789 | - |
790 | - def test_process_exception_aborts_transaction(self): |
791 | - abort = self.mocker.replace("transaction.abort") |
792 | - leave_transaction_management = self.mocker.replace( |
793 | - "django.db.transaction.leave_transaction_management") |
794 | - set_clean = self.mocker.replace( |
795 | - "django.db.transaction.set_clean") |
796 | - abort() |
797 | - set_clean() |
798 | - leave_transaction_management() |
799 | - self.mocker.replay() |
800 | - |
801 | - zope_middleware = ZopeTransactionMiddleware() |
802 | - request = HttpRequest() |
803 | - request.method = "GET" |
804 | - exception = RuntimeError("some error") |
805 | - zope_middleware.process_exception(request, exception) |
806 | - |
807 | - def test_process_response_commits_transaction_if_managed(self): |
808 | - commit = self.mocker.replace("transaction.commit") |
809 | - leave_transaction_management = self.mocker.replace( |
810 | - "django.db.transaction.leave_transaction_management") |
811 | - is_managed = self.mocker.replace( |
812 | - "django.db.transaction.is_managed") |
813 | - set_clean = self.mocker.replace( |
814 | - "django.db.transaction.set_clean") |
815 | - # We test three request methods |
816 | - self.expect(is_managed()).result(True).count(3) |
817 | - self.expect(commit()).count(3) |
818 | - self.expect(set_clean()).count(3) |
819 | - self.expect(leave_transaction_management()).count(3) |
820 | - self.mocker.replay() |
821 | - |
822 | - # Commit on all methods |
823 | - conf.settings.STORM_COMMIT_SAFE_METHODS = True |
824 | - |
825 | - zope_middleware = ZopeTransactionMiddleware() |
826 | - request = HttpRequest() |
827 | - response = HttpResponse() |
828 | - |
829 | - request.method = "GET" |
830 | - zope_middleware.process_response(request, response) |
831 | - request.method = "HEAD" |
832 | - zope_middleware.process_response(request, response) |
833 | - request.method = "POST" |
834 | - zope_middleware.process_response(request, response) |
835 | - |
836 | - def test_process_response_aborts_transaction_for_safe_methods(self): |
837 | - abort = self.mocker.replace("transaction.abort") |
838 | - commit = self.mocker.replace("transaction.commit") |
839 | - leave_transaction_management = self.mocker.replace( |
840 | - "django.db.transaction.leave_transaction_management") |
841 | - is_managed = self.mocker.replace( |
842 | - "django.db.transaction.is_managed") |
843 | - set_clean = self.mocker.replace( |
844 | - "django.db.transaction.set_clean") |
845 | - # We test three request methods |
846 | - self.expect(is_managed()).result(True).count(3) |
847 | - self.expect(abort()).count(2) |
848 | - commit() |
849 | - self.expect(set_clean()).count(3) |
850 | - self.expect(leave_transaction_management()).count(3) |
851 | - self.mocker.replay() |
852 | - |
853 | - # Don't commit on safe methods |
854 | - conf.settings.STORM_COMMIT_SAFE_METHODS = False |
855 | - |
856 | - zope_middleware = ZopeTransactionMiddleware() |
857 | - request = HttpRequest() |
858 | - response = HttpResponse() |
859 | - |
860 | - request.method = "GET" |
861 | - zope_middleware.process_response(request, response) |
862 | - request.method = "HEAD" |
863 | - zope_middleware.process_response(request, response) |
864 | - request.method = "POST" |
865 | - zope_middleware.process_response(request, response) |
866 | - |
867 | - def test_process_response_aborts_transaction_not_managed(self): |
868 | - abort = self.mocker.replace("transaction.abort") |
869 | - commit = self.mocker.replace("transaction.commit") |
870 | - leave_transaction_management = self.mocker.replace( |
871 | - "django.db.transaction.leave_transaction_management") |
872 | - is_managed = self.mocker.replace( |
873 | - "django.db.transaction.is_managed") |
874 | - set_clean = self.mocker.replace( |
875 | - "django.db.transaction.set_clean") |
876 | - |
877 | - self.expect(is_managed()).result(False).count(2) |
878 | - # None of these methods should be called |
879 | - self.expect(commit()).count(0) |
880 | - self.expect(abort()).count(0) |
881 | - self.expect(set_clean()).count(0) |
882 | - self.expect(leave_transaction_management()).count(0) |
883 | - self.mocker.replay() |
884 | - |
885 | - zope_middleware = ZopeTransactionMiddleware() |
886 | - request = HttpRequest() |
887 | - response = HttpResponse() |
888 | - |
889 | - request.method = "GET" |
890 | - zope_middleware.process_response(request, response) |
891 | - |
892 | - # Try the same with a safe method. |
893 | - conf.settings.STORM_COMMIT_SAFE_METHODS = False |
894 | - zope_middleware = ZopeTransactionMiddleware() |
895 | - zope_middleware.process_response(request, response) |
896 | - |
897 | - def test_process_response_aborts_transaction_on_failed_commit(self): |
898 | - _transaction = transaction.get() |
899 | - resource1 = self.mocker.mock() |
900 | - self.expect(resource1.prepare).throw(AttributeError).count(0) |
901 | - resource2 = self.mocker.mock() |
902 | - self.expect(resource2.prepare).throw(AttributeError).count(0) |
903 | - leave_transaction_management = self.mocker.replace( |
904 | - "django.db.transaction.leave_transaction_management") |
905 | - is_managed = self.mocker.replace( |
906 | - "django.db.transaction.is_managed") |
907 | - set_clean = self.mocker.replace( |
908 | - "django.db.transaction.set_clean") |
909 | - |
910 | - self.expect(is_managed()).result(True).count(1) |
911 | - self.expect(resource1.tpc_begin(_transaction)).throw(DisconnectionError) |
912 | - self.expect(resource1.abort(_transaction)).count(2) |
913 | - # None of these methods should be called |
914 | - self.expect(set_clean()).count(0) |
915 | - self.expect(leave_transaction_management()).count(0) |
916 | - self.mocker.replay() |
917 | - |
918 | - _transaction.join(resource1) |
919 | - zope_middleware = ZopeTransactionMiddleware() |
920 | - request = HttpRequest() |
921 | - response = HttpResponse() |
922 | - |
923 | - # Processing this response should fail on "commit()" with a |
924 | - # DisconnectionError. The error is saved by |
925 | - # "_saveAndRaiseCommitishError()", and re-raised, so we need to catch |
926 | - # it and abort the transaction. |
927 | - request.method = "POST" |
928 | - self.assertRaises(DisconnectionError, zope_middleware.process_response, |
929 | - request, response) |
930 | - |
931 | - # Now the transaction should have been cleared by "abort()". A resource |
932 | - # joining the transaction manager should not fail with a |
933 | - # "TransactionFailedError". |
934 | - transaction.get().join(resource2) |
935 | |
936 | === removed file 'tests/django/stores.py' |
937 | --- tests/django/stores.py 2015-01-25 09:29:29 +0000 |
938 | +++ tests/django/stores.py 1970-01-01 00:00:00 +0000 |
939 | @@ -1,109 +0,0 @@ |
940 | -# |
941 | -# Copyright (c) 2008 Canonical |
942 | -# |
943 | -# Written by James Henstridge <jamesh@canonical.com> |
944 | -# |
945 | -# This file is part of Storm Object Relational Mapper. |
946 | -# |
947 | -# Storm is free software; you can redistribute it and/or modify |
948 | -# it under the terms of the GNU Lesser General Public License as |
949 | -# published by the Free Software Foundation; either version 2.1 of |
950 | -# the License, or (at your option) any later version. |
951 | -# |
952 | -# Storm is distributed in the hope that it will be useful, |
953 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
954 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
955 | -# GNU Lesser General Public License for more details. |
956 | -# |
957 | -# You should have received a copy of the GNU Lesser General Public License |
958 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
959 | -# |
960 | - |
961 | -import threading |
962 | - |
963 | -try: |
964 | - import django |
965 | - import transaction |
966 | -except ImportError: |
967 | - have_django_and_transaction = False |
968 | -else: |
969 | - have_django_and_transaction = True |
970 | - from django import conf |
971 | - from storm.django import stores |
972 | - from storm.zope.zstorm import global_zstorm |
973 | - |
974 | -from storm.store import Store |
975 | -from tests.helper import TestHelper |
976 | - |
977 | - |
978 | -class DjangoStoreTests(TestHelper): |
979 | - |
980 | - def is_supported(self): |
981 | - return have_django_and_transaction |
982 | - |
983 | - def setUp(self): |
984 | - super(DjangoStoreTests, self).setUp() |
985 | - conf.settings.configure(STORM_STORES={}) |
986 | - |
987 | - def tearDown(self): |
988 | - if django.VERSION >= (1, 1): |
989 | - if django.VERSION >= (1, 6): |
990 | - from django.utils.functional import empty |
991 | - else: |
992 | - empty = None |
993 | - conf.settings._wrapped = empty |
994 | - else: |
995 | - conf.settings._target = None |
996 | - # Reset the utility to cleanup the StoreSynchronizer's from the |
997 | - # transaction. |
998 | - global_zstorm._reset() |
999 | - stores.have_configured_stores = False |
1000 | - # Free the transaction to avoid having errors that cross |
1001 | - # test cases. |
1002 | - transaction.manager.free(transaction.get()) |
1003 | - super(DjangoStoreTests, self).tearDown() |
1004 | - |
1005 | - def test_configure_stores_configures_store_uris(self): |
1006 | - conf.settings.MIDDLEWARE_CLASSES += ( |
1007 | - "storm.django.middleware.ZopeTransactionMiddleware",) |
1008 | - conf.settings.STORM_STORES = {"name1": "sqlite:1", |
1009 | - "name2": "sqlite:2", |
1010 | - "name3": "sqlite:3"} |
1011 | - stores.configure_stores(conf.settings) |
1012 | - default_uris = global_zstorm.get_default_uris() |
1013 | - self.assertEquals(default_uris, {"name1": "sqlite:1", |
1014 | - "name2": "sqlite:2", |
1015 | - "name3": "sqlite:3"}) |
1016 | - |
1017 | - def test_get_store(self): |
1018 | - conf.settings.MIDDLEWARE_CLASSES += ( |
1019 | - "storm.django.middleware.ZopeTransactionMiddleware",) |
1020 | - conf.settings.STORM_STORES = {"name": "sqlite:"} |
1021 | - store = stores.get_store("name") |
1022 | - self.assertTrue(isinstance(store, Store)) |
1023 | - # Calling get_store() twice returns the same store. |
1024 | - store2 = stores.get_store("name") |
1025 | - self.assertTrue(store is store2) |
1026 | - |
1027 | - def test_get_store_returns_per_thread_stores(self): |
1028 | - conf.settings.MIDDLEWARE_CLASSES += ( |
1029 | - "storm.django.middleware.ZopeTransactionMiddleware",) |
1030 | - conf.settings.STORM_STORES = {"name": "sqlite:"} |
1031 | - |
1032 | - store = stores.get_store("name") |
1033 | - other_stores = [] |
1034 | - def f(): |
1035 | - other_stores.append(stores.get_store("name")) |
1036 | - |
1037 | - thread = threading.Thread(target=f) |
1038 | - thread.start() |
1039 | - thread.join() |
1040 | - self.assertEqual(len(other_stores), 1) |
1041 | - self.assertNotEqual(other_stores[0], store) |
1042 | - |
1043 | - def test_get_store_uri(self): |
1044 | - conf.settings.MIDDLEWARE_CLASSES += ( |
1045 | - "storm.django.middleware.ZopeTransactionMiddleware",) |
1046 | - conf.settings.STORM_STORES = {"name": "sqlite:"} |
1047 | - |
1048 | - self.assertEqual(stores.get_store_uri("name"), "sqlite:") |
This branch looks good and the tests do in fact pass on Xenial for me.