From 4c18a8a3788a4cd25b75ae342a7244c5f775b213 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 24 Feb 2016 20:16:49 +0100 Subject: [PATCH] Fixed #14098 -- Prevented crash for introspection errors in inspectdb Thanks Tim Graham for the review. --- django/core/management/commands/inspectdb.py | 34 ++++++++++++-------- tests/inspectdb/tests.py | 17 +++++++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index 3bb8e759246..982ba7f1bd9 100644 --- a/django/core/management/commands/inspectdb.py +++ b/django/core/management/commands/inspectdb.py @@ -6,6 +6,7 @@ from collections import OrderedDict from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS, connections +from django.utils.encoding import force_text class Command(BaseCommand): @@ -57,25 +58,32 @@ class Command(BaseCommand): if table_name_filter is not None and callable(table_name_filter): if not table_name_filter(table_name): continue + try: + try: + relations = connection.introspection.get_relations(cursor, table_name) + except NotImplementedError: + relations = {} + try: + indexes = connection.introspection.get_indexes(cursor, table_name) + except NotImplementedError: + indexes = {} + try: + constraints = connection.introspection.get_constraints(cursor, table_name) + except NotImplementedError: + constraints = {} + table_description = connection.introspection.get_table_description(cursor, table_name) + except Exception as e: + yield "# Unable to inspect table '%s'" % table_name + yield "# The error was: %s" % force_text(e) + continue + yield '' yield '' yield 'class %s(models.Model):' % table2model(table_name) known_models.append(table2model(table_name)) - try: - relations = connection.introspection.get_relations(cursor, table_name) - except NotImplementedError: - relations = {} - try: - indexes = connection.introspection.get_indexes(cursor, table_name) - except NotImplementedError: - indexes = {} - try: - constraints = connection.introspection.get_constraints(cursor, table_name) - except NotImplementedError: - constraints = {} used_column_names = [] # Holds column names used in the table so far column_to_field_name = {} # Maps column names to names of model fields - for row in connection.introspection.get_table_description(cursor, table_name): + for row in table_description: comment_notes = [] # Holds Field notes, to be displayed in a Python comment. extra_params = OrderedDict() # Holds Field parameters such as 'db_column'. column_name = row[0] diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index 06eb2c82f89..6fd1778b558 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -6,7 +6,8 @@ from unittest import skipUnless from django.core.management import call_command from django.db import connection -from django.test import TestCase, skipUnlessDBFeature +from django.test import TestCase, mock, skipUnlessDBFeature +from django.utils.encoding import force_text from django.utils.six import PY3, StringIO from .models import ColumnTypes @@ -268,3 +269,17 @@ class InspectDBTestCase(TestCase): self.assertIn("big_int_field = models.BigIntegerField()", output) finally: connection.introspection.data_types_reverse = orig_data_types_reverse + + def test_introspection_errors(self): + """ + Introspection errors should not crash the command, and the error should + be visible in the output. + """ + out = StringIO() + with mock.patch('django.db.backends.base.introspection.BaseDatabaseIntrospection.table_names', + return_value=['nonexistent']): + call_command('inspectdb', stdout=out) + output = force_text(out.getvalue()) + self.assertIn("# Unable to inspect table 'nonexistent'", output) + # The error message depends on the backend + self.assertIn("# The error was:", output)