diff --git a/django/db/transaction.py b/django/db/transaction.py index cf7350c02f..a2977beabc 100644 --- a/django/db/transaction.py +++ b/django/db/transaction.py @@ -11,6 +11,7 @@ called, a commit is made. Managed transactions don't do those commits, but will need some kind of manual or implicit commits or rollbacks. """ +from __future__ import with_statement import sys from functools import wraps diff --git a/django/test/testcases.py b/django/test/testcases.py index 81e028c3dc..8ce3a7af0f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1,3 +1,5 @@ +from __future__ import with_statement + import re import sys from functools import wraps @@ -531,15 +533,8 @@ class TransactionTestCase(ut2.TestCase): return context # Basically emulate the `with` statement here. - - context.__enter__() - try: + with context: func(*args, **kwargs) - except: - context.__exit__(*sys.exc_info()) - raise - else: - context.__exit__(*sys.exc_info()) def connections_support_transactions(): """ diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py index f798f74df9..ca8965e15a 100644 --- a/tests/modeltests/files/models.py +++ b/tests/modeltests/files/models.py @@ -16,9 +16,6 @@ from django.core.files.storage import FileSystemStorage temp_storage_location = tempfile.mkdtemp() temp_storage = FileSystemStorage(location=temp_storage_location) -# Write out a file to be used as default content -temp_storage.save('tests/default.txt', ContentFile('default content')) - class Storage(models.Model): def custom_upload_to(self, filename): return 'foo' diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py index fce3b4dcd8..885f787984 100644 --- a/tests/modeltests/files/tests.py +++ b/tests/modeltests/files/tests.py @@ -1,14 +1,16 @@ +from __future__ import with_statement + import shutil import sys +import tempfile from django.core.cache import cache +from django.core.files import File from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase from models import Storage, temp_storage, temp_storage_location -if sys.version_info >= (2, 5): - from tests_25 import FileObjTests class FileTests(TestCase): @@ -16,6 +18,7 @@ class FileTests(TestCase): shutil.rmtree(temp_storage_location) def test_files(self): + temp_storage.save('tests/default.txt', ContentFile('default content')) # Attempting to access a FileField from the class raises a descriptive # error self.assertRaises(AttributeError, lambda: Storage.normal) @@ -103,3 +106,12 @@ class FileTests(TestCase): obj2.normal.delete() obj3.default.delete() obj4.random.delete() + + def test_context_manager(self): + orig_file = tempfile.TemporaryFile() + base_file = File(orig_file) + with base_file as f: + self.assertIs(base_file, f) + self.assertFalse(f.closed) + self.assertTrue(f.closed) + self.assertTrue(orig_file.closed) diff --git a/tests/modeltests/files/tests_25.py b/tests/modeltests/files/tests_25.py deleted file mode 100644 index 48eb6e26f7..0000000000 --- a/tests/modeltests/files/tests_25.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import with_statement - -import tempfile - -from django.core.files import File -from django.utils.unittest import TestCase - - -class FileObjTests(TestCase): - def test_context_manager(self): - orig_file = tempfile.TemporaryFile() - base_file = File(orig_file) - with base_file as f: - self.assertIs(base_file, f) - self.assertFalse(f.closed) - self.assertTrue(f.closed) - self.assertTrue(orig_file.closed) diff --git a/tests/modeltests/transactions/tests.py b/tests/modeltests/transactions/tests.py index 00ceed584b..db58b80e29 100644 --- a/tests/modeltests/transactions/tests.py +++ b/tests/modeltests/transactions/tests.py @@ -1,3 +1,5 @@ +from __future__ import with_statement + import sys from django.db import connection, transaction, IntegrityError, DEFAULT_DB_ALIAS @@ -7,10 +9,6 @@ from django.test import TransactionTestCase, skipUnlessDBFeature from models import Reporter -if sys.version_info >= (2, 5): - from tests_25 import TransactionContextManagerTests - - class TransactionTests(TransactionTestCase): def create_a_reporter_then_fail(self, first, last): a = Reporter(first_name=first, last_name=last) @@ -183,3 +181,133 @@ class TransactionRollbackTests(TransactionTestCase): execute_bad_sql = transaction.commit_on_success(self.execute_bad_sql) self.assertRaises(IntegrityError, execute_bad_sql) transaction.rollback() + +class TransactionContextManagerTests(TransactionTestCase): + def create_reporter_and_fail(self): + Reporter.objects.create(first_name="Bob", last_name="Holtzman") + raise Exception + + @skipUnlessDBFeature('supports_transactions') + def test_autocommit(self): + """ + The default behavior is to autocommit after each save() action. + """ + with self.assertRaises(Exception): + self.create_reporter_and_fail() + # The object created before the exception still exists + self.assertEqual(Reporter.objects.count(), 1) + + @skipUnlessDBFeature('supports_transactions') + def test_autocommit_context_manager(self): + """ + The autocommit context manager works exactly the same as the default + behavior. + """ + with self.assertRaises(Exception): + with transaction.autocommit(): + self.create_reporter_and_fail() + + self.assertEqual(Reporter.objects.count(), 1) + + @skipUnlessDBFeature('supports_transactions') + def test_autocommit_context_manager_with_using(self): + """ + The autocommit context manager also works with a using argument. + """ + with self.assertRaises(Exception): + with transaction.autocommit(using="default"): + self.create_reporter_and_fail() + + self.assertEqual(Reporter.objects.count(), 1) + + @skipUnlessDBFeature('supports_transactions') + def test_commit_on_success(self): + """ + With the commit_on_success context manager, the transaction is only + committed if the block doesn't throw an exception. + """ + with self.assertRaises(Exception): + with transaction.commit_on_success(): + self.create_reporter_and_fail() + + self.assertEqual(Reporter.objects.count(), 0) + + @skipUnlessDBFeature('supports_transactions') + def test_commit_on_success_with_using(self): + """ + The commit_on_success context manager also works with a using argument. + """ + with self.assertRaises(Exception): + with transaction.commit_on_success(using="default"): + self.create_reporter_and_fail() + + self.assertEqual(Reporter.objects.count(), 0) + + @skipUnlessDBFeature('supports_transactions') + def test_commit_on_success_succeed(self): + """ + If there aren't any exceptions, the data will get saved. + """ + Reporter.objects.create(first_name="Alice", last_name="Smith") + with transaction.commit_on_success(): + Reporter.objects.filter(first_name="Alice").delete() + + self.assertQuerysetEqual(Reporter.objects.all(), []) + + @skipUnlessDBFeature('supports_transactions') + def test_commit_on_success_exit(self): + with transaction.autocommit(): + with transaction.commit_on_success(): + Reporter.objects.create(first_name="Bobby", last_name="Tables") + + # Much more formal + r = Reporter.objects.get() + r.first_name = "Robert" + r.save() + + r = Reporter.objects.get() + self.assertEqual(r.first_name, "Robert") + + @skipUnlessDBFeature('supports_transactions') + def test_manually_managed(self): + """ + You can manually manage transactions if you really want to, but you + have to remember to commit/rollback. + """ + with transaction.commit_manually(): + Reporter.objects.create(first_name="Libby", last_name="Holtzman") + transaction.commit() + self.assertEqual(Reporter.objects.count(), 1) + + @skipUnlessDBFeature('supports_transactions') + def test_manually_managed_mistake(self): + """ + If you forget, you'll get bad errors. + """ + with self.assertRaises(transaction.TransactionManagementError): + with transaction.commit_manually(): + Reporter.objects.create(first_name="Scott", last_name="Browning") + + @skipUnlessDBFeature('supports_transactions') + def test_manually_managed_with_using(self): + """ + The commit_manually function also works with a using argument. + """ + with self.assertRaises(transaction.TransactionManagementError): + with transaction.commit_manually(using="default"): + Reporter.objects.create(first_name="Walter", last_name="Cronkite") + + @skipUnlessDBFeature('requires_rollback_on_dirty_transaction') + def test_bad_sql(self): + """ + Regression for #11900: If a block wrapped by commit_on_success + writes a transaction that can't be committed, that transaction should + be rolled back. The bug is only visible using the psycopg2 backend, + though the fix is generally a good idea. + """ + with self.assertRaises(IntegrityError): + with transaction.commit_on_success(): + cursor = connection.cursor() + cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');") + transaction.set_dirty() + transaction.rollback() diff --git a/tests/modeltests/transactions/tests_25.py b/tests/modeltests/transactions/tests_25.py deleted file mode 100644 index cc2290edff..0000000000 --- a/tests/modeltests/transactions/tests_25.py +++ /dev/null @@ -1,137 +0,0 @@ -from __future__ import with_statement - -from django.db import connection, transaction, IntegrityError -from django.test import TransactionTestCase, skipUnlessDBFeature - -from models import Reporter - - -class TransactionContextManagerTests(TransactionTestCase): - def create_reporter_and_fail(self): - Reporter.objects.create(first_name="Bob", last_name="Holtzman") - raise Exception - - @skipUnlessDBFeature('supports_transactions') - def test_autocommit(self): - """ - The default behavior is to autocommit after each save() action. - """ - with self.assertRaises(Exception): - self.create_reporter_and_fail() - # The object created before the exception still exists - self.assertEqual(Reporter.objects.count(), 1) - - @skipUnlessDBFeature('supports_transactions') - def test_autocommit_context_manager(self): - """ - The autocommit context manager works exactly the same as the default - behavior. - """ - with self.assertRaises(Exception): - with transaction.autocommit(): - self.create_reporter_and_fail() - - self.assertEqual(Reporter.objects.count(), 1) - - @skipUnlessDBFeature('supports_transactions') - def test_autocommit_context_manager_with_using(self): - """ - The autocommit context manager also works with a using argument. - """ - with self.assertRaises(Exception): - with transaction.autocommit(using="default"): - self.create_reporter_and_fail() - - self.assertEqual(Reporter.objects.count(), 1) - - @skipUnlessDBFeature('supports_transactions') - def test_commit_on_success(self): - """ - With the commit_on_success context manager, the transaction is only - committed if the block doesn't throw an exception. - """ - with self.assertRaises(Exception): - with transaction.commit_on_success(): - self.create_reporter_and_fail() - - self.assertEqual(Reporter.objects.count(), 0) - - @skipUnlessDBFeature('supports_transactions') - def test_commit_on_success_with_using(self): - """ - The commit_on_success context manager also works with a using argument. - """ - with self.assertRaises(Exception): - with transaction.commit_on_success(using="default"): - self.create_reporter_and_fail() - - self.assertEqual(Reporter.objects.count(), 0) - - @skipUnlessDBFeature('supports_transactions') - def test_commit_on_success_succeed(self): - """ - If there aren't any exceptions, the data will get saved. - """ - Reporter.objects.create(first_name="Alice", last_name="Smith") - with transaction.commit_on_success(): - Reporter.objects.filter(first_name="Alice").delete() - - self.assertQuerysetEqual(Reporter.objects.all(), []) - - @skipUnlessDBFeature('supports_transactions') - def test_commit_on_success_exit(self): - with transaction.autocommit(): - with transaction.commit_on_success(): - Reporter.objects.create(first_name="Bobby", last_name="Tables") - - # Much more formal - r = Reporter.objects.get() - r.first_name = "Robert" - r.save() - - r = Reporter.objects.get() - self.assertEqual(r.first_name, "Robert") - - @skipUnlessDBFeature('supports_transactions') - def test_manually_managed(self): - """ - You can manually manage transactions if you really want to, but you - have to remember to commit/rollback. - """ - with transaction.commit_manually(): - Reporter.objects.create(first_name="Libby", last_name="Holtzman") - transaction.commit() - self.assertEqual(Reporter.objects.count(), 1) - - @skipUnlessDBFeature('supports_transactions') - def test_manually_managed_mistake(self): - """ - If you forget, you'll get bad errors. - """ - with self.assertRaises(transaction.TransactionManagementError): - with transaction.commit_manually(): - Reporter.objects.create(first_name="Scott", last_name="Browning") - - @skipUnlessDBFeature('supports_transactions') - def test_manually_managed_with_using(self): - """ - The commit_manually function also works with a using argument. - """ - with self.assertRaises(transaction.TransactionManagementError): - with transaction.commit_manually(using="default"): - Reporter.objects.create(first_name="Walter", last_name="Cronkite") - - @skipUnlessDBFeature('requires_rollback_on_dirty_transaction') - def test_bad_sql(self): - """ - Regression for #11900: If a block wrapped by commit_on_success - writes a transaction that can't be committed, that transaction should - be rolled back. The bug is only visible using the psycopg2 backend, - though the fix is generally a good idea. - """ - with self.assertRaises(IntegrityError): - with transaction.commit_on_success(): - cursor = connection.cursor() - cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');") - transaction.set_dirty() - transaction.rollback() diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index d5dd739782..8d23cf5501 100644 --- a/tests/regressiontests/test_utils/tests.py +++ b/tests/regressiontests/test_utils/tests.py @@ -1,12 +1,11 @@ +from __future__ import with_statement + import sys from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature from models import Person -if sys.version_info >= (2, 5): - from tests_25 import AssertNumQueriesContextManagerTests - class SkippingTestCase(TestCase): def test_skip_unless_db_feature(self): @@ -48,6 +47,41 @@ class AssertNumQueriesTests(TestCase): self.client.get("/test_utils/get_person/%s/" % person.pk) self.assertNumQueries(2, test_func) +class AssertNumQueriesContextManagerTests(TestCase): + def test_simple(self): + with self.assertNumQueries(0): + pass + + with self.assertNumQueries(1): + Person.objects.count() + + with self.assertNumQueries(2): + Person.objects.count() + Person.objects.count() + + def test_failure(self): + with self.assertRaises(AssertionError) as exc_info: + with self.assertNumQueries(2): + Person.objects.count() + self.assertIn("1 queries executed, 2 expected", str(exc_info.exception)) + + with self.assertRaises(TypeError): + with self.assertNumQueries(4000): + raise TypeError + + def test_with_client(self): + person = Person.objects.create(name="test") + + with self.assertNumQueries(1): + self.client.get("/test_utils/get_person/%s/" % person.pk) + + with self.assertNumQueries(1): + self.client.get("/test_utils/get_person/%s/" % person.pk) + + with self.assertNumQueries(2): + self.client.get("/test_utils/get_person/%s/" % person.pk) + self.client.get("/test_utils/get_person/%s/" % person.pk) + class SaveRestoreWarningState(TestCase): def test_save_restore_warnings_state(self): diff --git a/tests/regressiontests/test_utils/tests_25.py b/tests/regressiontests/test_utils/tests_25.py deleted file mode 100644 index 9fe9c838e5..0000000000 --- a/tests/regressiontests/test_utils/tests_25.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import with_statement - -from django.test import TestCase - -from models import Person - - -class AssertNumQueriesContextManagerTests(TestCase): - def test_simple(self): - with self.assertNumQueries(0): - pass - - with self.assertNumQueries(1): - Person.objects.count() - - with self.assertNumQueries(2): - Person.objects.count() - Person.objects.count() - - def test_failure(self): - with self.assertRaises(AssertionError) as exc_info: - with self.assertNumQueries(2): - Person.objects.count() - self.assertIn("1 queries executed, 2 expected", str(exc_info.exception)) - - with self.assertRaises(TypeError): - with self.assertNumQueries(4000): - raise TypeError - - def test_with_client(self): - person = Person.objects.create(name="test") - - with self.assertNumQueries(1): - self.client.get("/test_utils/get_person/%s/" % person.pk) - - with self.assertNumQueries(1): - self.client.get("/test_utils/get_person/%s/" % person.pk) - - with self.assertNumQueries(2): - self.client.get("/test_utils/get_person/%s/" % person.pk) - self.client.get("/test_utils/get_person/%s/" % person.pk)