diff --git a/.gitignore b/.gitignore index 10e8061ef3..9c00274f12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,51 @@ -*.egg-info -*.py[co] -build/ +*.py[cod] *.sw[op] -.tox/ + +# Packages +*.egg +*.egg-info +dist +build +bin +.tox MANIFEST -dist/ django_tests +__pycache__ + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.nox +.cache +.pytest_cache + + +# Mac +.DS_Store + +# JetBrains +.idea + +# VS Code +.vscode + +# emacs +*~ + +# Built documentation +docs/_build +bigquery/docs/generated + +# Virtual environment +env/ +coverage.xml +sponge_log.xml + +# System test environment variables. +system_tests/local_test_setup + +# Make sure a generated file isn't accidentally committed. +pylintrc +pylintrc.test \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 55ff6105d0..0000000000 --- a/README.md +++ /dev/null @@ -1,203 +0,0 @@ -# django-spanner -ORM plugin for using Cloud Spanner as a database for Django. - -# 🚨THIS CODE IS STILL UNDER DEVELOPMENT🚨 - -## Table of contents -- [Installing it](#installing-it) -- [Using it](#using-it) - - [Format](#format) - - [Example](#example) -- [Functional tests](#functional-tests) -- [Django integration tests](#django-integration-tests) - - [django_test_suite.sh](#django_test_suitesh) - - [Environment variables](#environment-variables) - - [Example run](#example-run) - - [Parallelization script](#parallelization-script) - - [Environment variables](#environment-variables) - - [Example run](#example-run) -- [Limitations](#limitations) -- [How it works](#how-it-works) - - [Overall design](#overall-design) - - [Internals](#internals) - - -## Installing it - -Use the version of django-spanner that corresponds to your version of Django. -For example, django-spanner 2.2.x works with Django 2.2.y. (This is the only -supported version at this time.) - -The minor release number of Django doesn't correspond to the minor release -number of django-spanner. Use the latest minor release of each. - -```shell -pip3 install --user . -``` - -## Using it -After [installing it](#installing-it), you'll need to edit your Django `settings.py` file: - -* Add `django_spanner` as the very first entry in the `INSTALLED_APPS` setting -```python -INSTALLED_APPS = [ - 'django_spanner', - ... -] -``` - -* Edit the `DATABASES` setting to point to an EXISTING database - -### Format - -```python -DATABASES = { - 'default': { - 'ENGINE': 'django_spanner', - 'PROJECT': '', - 'INSTANCE': '', - 'NAME': '', - # Only include this if you need to specify where to retrieve the - # service account JSON for the credentials to connect to Cloud Spanner. - 'OPTIONS': { - 'credentials_uri': '', - }, - }, -} -``` - -### Example -For example: - -```python -DATABASES = { - 'default': { - 'ENGINE': 'django_spanner', - 'PROJECT': 'appdev-soda-spanner-staging', # Or the GCP project-id - 'INSTANCE': 'django-dev1', # Or the Cloud Spanner instance - 'NAME': 'db1', # Or the Cloud Spanner database to use - } -} -``` - -## Limitations - -### Transaction management isn't supported - -django-spanner always works in Django's default transaction behavior, -`autocommit` mode. Transactions cannot be controlled manually with -calls like `django.db.transaction.atomic()`. - -### `AutoField` generates random IDs - -Spanner doesn't have support for auto-generating primary key values. Therefore, -django-spanner monkey-patches `AutoField` to generate a random UUID4. It -generates a default using `Field`'s `default` option which means `AutoField`s -will have a value when a model instance is created. For example: - -``` ->>> ExampleModel() ->>> ExampleModel.pk -4229421414948291880 -``` - -To avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key), -these IDs are not monotonically increasing. This means that sorting models by -ID isn't guaranteed to return them in the order in which they were created. - -### `ForeignKey` constraints aren't created - -Spanner doesn't support `ON DELETE CASCADE` when creating foreign-key constraints so -django-spanner [doesn't support foreign key constraints](https://github.com/googleapis/python-spanner-django/issues/313). - -### Check constraints aren't supported - -Spanner doesn't support `CHECK` constraints so one isn't created for -[`PositiveIntegerField`](https://docs.djangoproject.com/en/stable/ref/models/fields/#positiveintegerfield) -and [`CheckConstraint`](https://docs.djangoproject.com/en/stable/ref/models/constraints/#checkconstraint) -can't be used. - -### `DecimalField` isn't supported - -Spanner doesn't support a NUMERIC data type that allows storing high precision -decimal values without the possibility of data loss. - -### `Variance` and `StdDev` database functions aren't supported - -Spanner doesn't support these functions. - -### `Meta.order_with_respect_to` model option isn't supported - -This feature uses a column name that starts with an underscore (`_order`) which -Spanner doesn't allow. - -### Random `QuerySet` ordering isn't supported - -Spanner doesn't support it. For example: - -``` ->>> ExampleModel.objects.order_by('?') -... -django.db.utils.ProgrammingError: 400 Function not found: RANDOM ... FROM -example_model ORDER BY RANDOM() ASC -``` - -### Schema migrations - -Spanner has some limitations on schema changes which you must respect: - -* Renaming tables and columns isn't supported. -* A column's type can't be changed. -* A table's primary key can't be altered. -* Migrations aren't atomic since django-spanner doesn't support transactions. - -### `DurationField` arithmetic doesn't work with `DateField` values ([#253](https://github.com/googleapis/python-spanner-django/issues/253)) - -Spanner requires using different functions for arithmetic depending on the -column type: - -* `TIMESTAMP` columns (`DateTimeField`) require `TIMESTAMP_ADD` or - `TIMESTAMP_SUB` -* `DATE` columns (`DateField`) require `DATE_ADD` or `DATE_SUB` - -Django doesn't provide a way to determine which database function to use. -`DatabaseOperations.combine_duration_expression()` arbitrary uses -`TIMESTAMP_ADD` and `TIMESTAMP_SUB`. Therefore, if you use a `DateField` in a -`DurationField` expression, you'll see an error like: "No matching signature -for function TIMESTAMP_ADD for argument types: DATE, INTERVAL INT64 -DATE_TIME_PART." - -### Computations that yield FLOAT64 values can't be assigned to INT64 columns - -Spanner [doesn't support this](https://github.com/googleapis/python-spanner-django/issues/331). - -For example, if `integer` is `IntegerField`: - -``` ->>> ExampleModel.objects.update(integer=F('integer') / 2) -... -django.db.utils.ProgrammingError: 400 Value of type FLOAT64 cannot be -assigned to integer, which has type INT64 [at 1:46]\nUPDATE -example_model SET integer = (example_model.integer /... -``` - -### Addition with null values crash - -For example: - -``` ->>> Book.objects.annotate(adjusted_rating=F('rating') + None) -... -google.api_core.exceptions.InvalidArgument: 400 Operands of + cannot be literal -NULL ... -``` - -## How it works - -### Overall design -![](./assets/overview.png) - -### Internals -![](./assets/internals.png) - -# 🚨THIS CODE IS STILL UNDER DEVELOPMENT🚨 diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000..c70ab20d68 --- /dev/null +++ b/README.rst @@ -0,0 +1,253 @@ +django-spanner +============== + +ORM plugin for using Cloud Spanner as a database for Django. + +🚨THIS CODE IS STILL UNDER DEVELOPMENT🚨 +======================================== + +Table of contents +----------------- + +- `Installing it <#installing-it>`__ +- `Using it <#using-it>`__ + + - `Format <#format>`__ + - `Example <#example>`__ + +- `Functional tests <#functional-tests>`__ +- `Django integration tests <#django-integration-tests>`__ + + - `django\_test\_suite.sh <#django_test_suitesh>`__ + + - `Environment variables <#environment-variables>`__ + - `Example run <#example-run>`__ + + - `Parallelization script <#parallelization-script>`__ + + - `Environment variables <#environment-variables>`__ + - `Example run <#example-run>`__ + +- `Limitations <#limitations>`__ +- `How it works <#how-it-works>`__ + + - `Overall design <#overall-design>`__ + - `Internals <#internals>`__ + +Installing it +------------- + +Use the version of django-spanner that corresponds to your version of +Django. For example, django-spanner 2.2.x works with Django 2.2.y. (This +is the only supported version at this time.) + +The minor release number of Django doesn't correspond to the minor +release number of django-spanner. Use the latest minor release of each. + +.. code:: shell + + pip3 install --user . + +Using it +-------- + +After `installing it <#installing-it>`__, you'll need to edit your +Django ``settings.py`` file: + +- Add ``django_spanner`` as the very first entry in the + ``INSTALLED_APPS`` setting + + .. code:: python + + INSTALLED_APPS = [ + 'spanner_django', + ... + ] + +- Edit the ``DATABASES`` setting to point to an EXISTING database + +Format +~~~~~~ + +.. code:: python + + DATABASES = { + 'default': { + 'ENGINE': 'spanner_django', + 'PROJECT': '', + 'INSTANCE': '', + 'NAME': '', + # Only include this if you need to specify where to retrieve the + # service account JSON for the credentials to connect to Cloud Spanner. + 'OPTIONS': { + 'credentials_uri': '', + }, + }, + } + +Example +~~~~~~~ + +For example: + +.. code:: python + + DATABASES = { + 'default': { + 'ENGINE': 'spanner_django', + 'PROJECT': 'appdev-soda-spanner-staging', # Or the GCP project-id + 'INSTANCE': 'django-dev1', # Or the Cloud Spanner instance + 'NAME': 'db1', # Or the Cloud Spanner database to use + } + } + +Limitations +----------- + +Transaction management isn't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +django-spanner always works in Django's default transaction behavior, +``autocommit`` mode. Transactions cannot be controlled manually with +calls like ``django.db.transaction.atomic()``. + +``AutoField`` generates random IDs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't have support for auto-generating primary key values. +Therefore, django-spanner monkey-patches ``AutoField`` to generate a +random UUID4. It generates a default using ``Field``'s ``default`` +option which means ``AutoField``\ s will have a value when a model +instance is created. For example: + +:: + + >>> ExampleModel() + >>> ExampleModel.pk + 4229421414948291880 + +To avoid +`hotspotting `__, +these IDs are not monotonically increasing. This means that sorting +models by ID isn't guaranteed to return them in the order in which they +were created. + +``ForeignKey`` constraints aren't created +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't support ``ON DELETE CASCADE`` when creating foreign-key +constraints so django-spanner `doesn't support foreign key +constraints `__. + +Check constraints aren't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't support ``CHECK`` constraints so one isn't created for +```PositiveIntegerField`` `__ +and +```CheckConstraint`` `__ +can't be used. + +``DecimalField`` isn't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't support a NUMERIC data type that allows storing high +precision decimal values without the possibility of data loss. + +``Variance`` and ``StdDev`` database functions aren't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't support these functions. + +``Meta.order_with_respect_to`` model option isn't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This feature uses a column name that starts with an underscore +(``_order``) which Spanner doesn't allow. + +Random ``QuerySet`` ordering isn't supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner doesn't support it. For example: + +:: + + >>> ExampleModel.objects.order_by('?') + ... + django.db.utils.ProgrammingError: 400 Function not found: RANDOM ... FROM + example_model ORDER BY RANDOM() ASC + +Schema migrations +~~~~~~~~~~~~~~~~~ + +Spanner has some limitations on schema changes which you must respect: + +- Renaming tables and columns isn't supported. +- A column's type can't be changed. +- A table's primary key can't be altered. +- Migrations aren't atomic since django-spanner doesn't support + transactions. + +``DurationField`` arithmetic doesn't work with ``DateField`` values (`#253 `__) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner requires using different functions for arithmetic depending on +the column type: + +- ``TIMESTAMP`` columns (``DateTimeField``) require ``TIMESTAMP_ADD`` + or ``TIMESTAMP_SUB`` +- ``DATE`` columns (``DateField``) require ``DATE_ADD`` or ``DATE_SUB`` + +Django doesn't provide a way to determine which database function to +use. ``DatabaseOperations.combine_duration_expression()`` arbitrary uses +``TIMESTAMP_ADD`` and ``TIMESTAMP_SUB``. Therefore, if you use a +``DateField`` in a ``DurationField`` expression, you'll see an error +like: "No matching signature for function TIMESTAMP\_ADD for argument +types: DATE, INTERVAL INT64 DATE\_TIME\_PART." + +Computations that yield FLOAT64 values can't be assigned to INT64 columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Spanner `doesn't support +this `__. + +For example, if ``integer`` is ``IntegerField``: + +:: + + >>> ExampleModel.objects.update(integer=F('integer') / 2) + ... + django.db.utils.ProgrammingError: 400 Value of type FLOAT64 cannot be + assigned to integer, which has type INT64 [at 1:46]\nUPDATE + example_model SET integer = (example_model.integer /... + +Addition with null values crash +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +:: + + >>> Book.objects.annotate(adjusted_rating=F('rating') + None) + ... + google.api_core.exceptions.InvalidArgument: 400 Operands of + cannot be literal + NULL ... + +How it works +------------ + +Overall design +~~~~~~~~~~~~~~ + +.. figure:: ./assets/overview.png + :alt: + +Internals +~~~~~~~~~ + +.. figure:: ./assets/internals.png + :alt: + +🚨🚨THIS CODE IS STILL UNDER DEVELOPMENT🚨🚨 +============================================ + diff --git a/django_spanner/features.py b/django_spanner/features.py deleted file mode 100644 index 22208757c1..0000000000 --- a/django_spanner/features.py +++ /dev/null @@ -1,1774 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -import os - -from django.db.backends.base.features import BaseDatabaseFeatures -from django.db.utils import InterfaceError - - -class DatabaseFeatures(BaseDatabaseFeatures): - can_introspect_big_integer_field = False - can_introspect_duration_field = False - can_introspect_foreign_keys = False - # TimeField is introspected as DateTimeField because they both use - # TIMESTAMP. - can_introspect_time_field = False - closed_cursor_error_class = InterfaceError - # Spanner uses REGEXP_CONTAINS which is case-sensitive. - has_case_insensitive_like = False - # https://cloud.google.com/spanner/quotas#query_limits - max_query_params = 950 - supports_foreign_keys = False - supports_ignore_conflicts = False - supports_partial_indexes = False - supports_regex_backreferencing = False - supports_select_for_update_with_limit = False - supports_sequence_reset = False - supports_timezones = False - supports_transactions = False - supports_column_check_constraints = False - supports_table_check_constraints = False - uses_savepoints = False - - # Django tests that aren't supported by Spanner. - skip_tests = ( - # No foreign key constraints in Spanner. - 'backends.tests.FkConstraintsTests.test_check_constraints', - 'fixtures_regress.tests.TestFixtures.test_loaddata_raises_error_when_fixture_has_invalid_foreign_key', - # No Django transaction management in Spanner. - 'basic.tests.SelectOnSaveTests.test_select_on_save_lying_update', - # django_spanner monkey patches AutoField to have a default value. - 'basic.tests.ModelTest.test_hash', - 'generic_relations.test_forms.GenericInlineFormsetTests.test_options', - 'generic_relations.tests.GenericRelationsTests.test_unsaved_instance_on_generic_foreign_key', - 'generic_relations_regress.tests.GenericRelationTests.test_target_model_is_unsaved', - 'm2m_through_regress.tests.ToFieldThroughTests.test_m2m_relations_unusable_on_null_pk_obj', - 'many_to_many.tests.ManyToManyTests.test_add', - 'many_to_one.tests.ManyToOneTests.test_fk_assignment_and_related_object_cache', - 'many_to_one.tests.ManyToOneTests.test_relation_unsaved', - 'model_fields.test_durationfield.TestSerialization.test_dumping', - 'model_fields.test_uuid.TestSerialization.test_dumping', - 'model_fields.test_booleanfield.ValidationTest.test_nullbooleanfield_blank', - 'model_inheritance.tests.ModelInheritanceTests.test_create_child_no_update', - 'model_regress.tests.ModelTests.test_get_next_prev_by_field_unsaved', - 'one_to_one.tests.OneToOneTests.test_get_reverse_on_unsaved_object', - 'one_to_one.tests.OneToOneTests.test_set_reverse_on_unsaved_object', - 'one_to_one.tests.OneToOneTests.test_unsaved_object', - 'queries.test_bulk_update.BulkUpdateNoteTests.test_unsaved_models', - 'serializers.test_json.JsonSerializerTestCase.test_pkless_serialized_strings', - 'serializers.test_json.JsonSerializerTestCase.test_serialize_with_null_pk', - 'serializers.test_xml.XmlSerializerTestCase.test_pkless_serialized_strings', - 'serializers.test_xml.XmlSerializerTestCase.test_serialize_with_null_pk', - 'serializers.test_yaml.YamlSerializerTestCase.test_pkless_serialized_strings', - 'serializers.test_yaml.YamlSerializerTestCase.test_serialize_with_null_pk', - 'timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime', - 'timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_naive_datetime', - 'validation.test_custom_messages.CustomMessagesTests.test_custom_null_message', - 'validation.test_custom_messages.CustomMessagesTests.test_custom_simple_validator_message', - 'validation.test_unique.PerformUniqueChecksTest.test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified', # noqa - 'validation.test_unique.PerformUniqueChecksTest.test_primary_key_unique_check_not_performed_when_not_adding', - 'validation.test_validators.TestModelsWithValidators.test_custom_validator_passes_for_correct_value', - 'validation.test_validators.TestModelsWithValidators.test_custom_validator_raises_error_for_incorrect_value', - 'validation.test_validators.TestModelsWithValidators.test_field_validators_can_be_any_iterable', - # Tests that assume a serial pk. - 'admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter_nullbooleanfield', - 'admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter_tuple', - 'admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter', - 'admin_filters.tests.ListFiltersTests.test_datefieldlistfilter_with_time_zone_support', - 'admin_filters.tests.ListFiltersTests.test_datefieldlistfilter', - 'admin_filters.tests.ListFiltersTests.test_fieldlistfilter_underscorelookup_tuple', - 'admin_filters.tests.ListFiltersTests.test_fk_with_to_field', - 'admin_filters.tests.ListFiltersTests.test_listfilter_genericrelation', - 'admin_filters.tests.ListFiltersTests.test_lookup_with_non_string_value_underscored', - 'admin_filters.tests.ListFiltersTests.test_lookup_with_non_string_value', - 'admin_filters.tests.ListFiltersTests.test_relatedfieldlistfilter_manytomany', - 'admin_filters.tests.ListFiltersTests.test_simplelistfilter', - 'admin_inlines.tests.TestInline.test_inline_hidden_field_no_column', - 'admin_utils.test_logentry.LogEntryTests.test_logentry_change_message', - 'admin_utils.test_logentry.LogEntryTests.test_logentry_change_message_localized_datetime_input', - 'admin_utils.test_logentry.LogEntryTests.test_proxy_model_content_type_is_used_for_log_entries', - 'admin_views.tests.AdminViewPermissionsTest.test_history_view', - 'aggregation.test_filter_argument.FilteredAggregateTests.test_plain_annotate', - 'aggregation.tests.AggregateTestCase.test_annotate_basic', - 'aggregation.tests.AggregateTestCase.test_annotation', - 'aggregation.tests.AggregateTestCase.test_filtering', - 'aggregation_regress.tests.AggregationTests.test_more_more', - 'aggregation_regress.tests.AggregationTests.test_more_more_more', - 'aggregation_regress.tests.AggregationTests.test_ticket_11293', - 'defer_regress.tests.DeferRegressionTest.test_ticket_23270', - 'distinct_on_fields.tests.DistinctOnTests.test_basic_distinct_on', - 'extra_regress.tests.ExtraRegressTests.test_regression_7314_7372', - 'generic_relations_regress.tests.GenericRelationTests.test_annotate', - 'get_earliest_or_latest.tests.TestFirstLast', - 'known_related_objects.tests.ExistingRelatedInstancesTests.test_reverse_one_to_one_multi_prefetch_related', - 'known_related_objects.tests.ExistingRelatedInstancesTests.test_reverse_one_to_one_multi_select_related', - 'lookup.tests.LookupTests.test_get_next_previous_by', - 'lookup.tests.LookupTests.test_values_list', - 'migrations.test_operations.OperationTests.test_alter_order_with_respect_to', - 'model_fields.tests.GetChoicesOrderingTests.test_get_choices_reverse_related_field', - 'model_formsets.tests.ModelFormsetTest.test_custom_pk', - 'model_formsets_regress.tests.FormfieldShouldDeleteFormTests.test_custom_delete', - 'multiple_database.tests.RouterTestCase.test_generic_key_cross_database_protection', - 'ordering.tests.OrderingTests.test_default_ordering_by_f_expression', - 'ordering.tests.OrderingTests.test_order_by_fk_attname', - 'ordering.tests.OrderingTests.test_order_by_override', - 'ordering.tests.OrderingTests.test_order_by_pk', - 'prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_m2m_then_m2m', - 'prefetch_related.tests.CustomPrefetchTests.test_custom_qs', - 'prefetch_related.tests.CustomPrefetchTests.test_nested_prefetch_related_are_not_overwritten', - 'prefetch_related.tests.DirectPrefechedObjectCacheReuseTests.test_detect_is_fetched', - 'prefetch_related.tests.DirectPrefechedObjectCacheReuseTests.test_detect_is_fetched_with_to_attr', - 'prefetch_related.tests.ForeignKeyToFieldTest.test_m2m', - 'queries.test_bulk_update.BulkUpdateNoteTests.test_multiple_fields', - 'queries.test_bulk_update.BulkUpdateTests.test_inherited_fields', - 'queries.tests.Queries1Tests.test_ticket9411', - 'queries.tests.Queries4Tests.test_ticket15316_exclude_true', - 'queries.tests.Queries5Tests.test_ticket7256', - 'queries.tests.SubqueryTests.test_related_sliced_subquery', - 'queries.tests.Ticket14056Tests.test_ticket_14056', - 'queries.tests.RelatedLookupTypeTests.test_values_queryset_lookup', - 'raw_query.tests.RawQueryTests.test_annotations', - 'raw_query.tests.RawQueryTests.test_get_item', - 'select_related.tests.SelectRelatedTests.test_field_traversal', - 'syndication_tests.tests.SyndicationFeedTest.test_rss2_feed', - 'syndication_tests.tests.SyndicationFeedTest.test_latest_post_date', - 'syndication_tests.tests.SyndicationFeedTest.test_rss091_feed', - 'syndication_tests.tests.SyndicationFeedTest.test_template_feed', - # datetimes retrieved from the database with the wrong hour when - # USE_TZ = True: https://github.com/orijtech/spanner-orm/issues/193 - 'datetimes.tests.DateTimesTests.test_21432', - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_func_with_timezone', - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_timezone_applied_before_truncation', # noqa - # extract() with timezone not working as expected: - # https://github.com/orijtech/spanner-orm/issues/191 - 'timezones.tests.NewDatabaseTests.test_query_datetimes', - # using NULL with + crashes: https://github.com/orijtech/spanner-orm/issues/201 - 'annotations.tests.NonAggregateAnnotationTestCase.test_combined_annotation_commutative', - # Spanner loses DecimalField precision due to conversion to float: - # https://github.com/orijtech/spanner-orm/pull/133#pullrequestreview-328482925 - 'aggregation.tests.AggregateTestCase.test_decimal_max_digits_has_no_effect', - 'aggregation.tests.AggregateTestCase.test_related_aggregate', - 'db_functions.comparison.test_cast.CastTests.test_cast_to_decimal_field', - 'model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding', - 'model_fields.test_decimalfield.DecimalFieldTests.test_roundtrip_with_trailing_zeros', - # No CHECK constraints in Spanner. - 'model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values', - # Spanner doesn't support the variance the standard deviation database - # functions: - 'aggregation.test_filter_argument.FilteredAggregateTests.test_filtered_numerical_aggregates', - 'aggregation_regress.tests.AggregationTests.test_stddev', - # SELECT list expression references which is neither grouped - # nor aggregated: https://github.com/orijtech/spanner-orm/issues/245 - 'aggregation_regress.tests.AggregationTests.test_annotated_conditional_aggregate', - 'aggregation_regress.tests.AggregationTests.test_annotation_with_value', - 'expressions.tests.BasicExpressionsTests.test_filtering_on_annotate_that_uses_q', - # "No matching signature for operator" crash when comparing TIMESTAMP - # and DATE: https://github.com/orijtech/django-spanner/issues/255 - 'expressions.tests.BasicExpressionsTests.test_outerref_mixed_case_table_name', - 'expressions.tests.FTimeDeltaTests.test_mixed_comparisons1', - # duration arithmetic fails with dates: No matching signature for - # function TIMESTAMP_ADD: https://github.com/orijtech/django-spanner/issues/253 - 'expressions.tests.FTimeDeltaTests.test_date_comparison', - 'expressions.tests.FTimeDeltaTests.test_date_minus_duration', - 'expressions.tests.FTimeDeltaTests.test_delta_add', - 'expressions.tests.FTimeDeltaTests.test_duration_with_datetime', - 'expressions.tests.FTimeDeltaTests.test_mixed_comparisons2', - # This test doesn't raise NotSupportedError because Spanner doesn't - # support select for update either (besides the "with limit" - # restriction). - 'select_for_update.tests.SelectForUpdateTests.test_unsupported_select_for_update_with_limit', - # integer division produces a float result, which can't be assigned to - # an integer column: - # https://github.com/orijtech/django-spanner/issues/331 - 'expressions.tests.ExpressionOperatorTests.test_lefthand_division', - 'expressions.tests.ExpressionOperatorTests.test_right_hand_division', - # power operator produces a float result, which can't be assigned to - # an integer column: - # https://github.com/orijtech/django-spanner/issues/331 - 'expressions.tests.ExpressionOperatorTests.test_lefthand_power', - 'expressions.tests.ExpressionOperatorTests.test_righthand_power', - # Cloud Spanner's docs: "The rows that are returned by LIMIT and OFFSET - # is unspecified unless these operators are used after ORDER BY." - 'aggregation_regress.tests.AggregationTests.test_sliced_conditional_aggregate', - 'queries.tests.QuerySetBitwiseOperationTests.test_or_with_both_slice', - 'queries.tests.QuerySetBitwiseOperationTests.test_or_with_both_slice_and_ordering', - 'queries.tests.QuerySetBitwiseOperationTests.test_or_with_lhs_slice', - 'queries.tests.QuerySetBitwiseOperationTests.test_or_with_rhs_slice', - 'queries.tests.SubqueryTests.test_slice_subquery_and_query', - # Cloud Spanner limit: "Number of functions exceeds the maximum - # allowed limit of 1000." - 'queries.test_bulk_update.BulkUpdateTests.test_large_batch', - # Spanner doesn't support random ordering. - 'ordering.tests.OrderingTests.test_random_ordering', - # No matching signature for function MOD for argument types: FLOAT64, - # FLOAT64. Supported signatures: MOD(INT64, INT64) - 'db_functions.math.test_mod.ModTests.test_decimal', - 'db_functions.math.test_mod.ModTests.test_float', - # casting DateField to DateTimeField adds an unexpected hour: - # https://github.com/orijtech/spanner-orm/issues/260 - 'db_functions.comparison.test_cast.CastTests.test_cast_from_db_date_to_datetime', - # Tests that fail during tear down on databases that don't support - # transactions: https://github.com/orijtech/spanner-orm/issues/271 - 'admin_views.test_multidb.MultiDatabaseTests.test_add_view', - 'admin_views.test_multidb.MultiDatabaseTests.test_change_view', - 'admin_views.test_multidb.MultiDatabaseTests.test_delete_view', - 'auth_tests.test_admin_multidb.MultiDatabaseTests.test_add_view', - 'contenttypes_tests.test_models.ContentTypesMultidbTests.test_multidb', - # Tests that by-pass using django_spanner and generate - # invalid DDL: https://github.com/orijtech/django-spanner/issues/298 - 'cache.tests.CreateCacheTableForDBCacheTests', - 'cache.tests.DBCacheTests', - 'cache.tests.DBCacheWithTimeZoneTests', - # Tests that require transactions. - 'transaction_hooks.tests.TestConnectionOnCommit.test_does_not_execute_if_transaction_rolled_back', - 'transaction_hooks.tests.TestConnectionOnCommit.test_hooks_cleared_after_rollback', - 'transaction_hooks.tests.TestConnectionOnCommit.test_hooks_cleared_on_reconnect', - 'transaction_hooks.tests.TestConnectionOnCommit.test_no_hooks_run_from_failed_transaction', - 'transaction_hooks.tests.TestConnectionOnCommit.test_no_savepoints_atomic_merged_with_outer', - # Tests that require savepoints. - 'get_or_create.tests.UpdateOrCreateTests.test_integrity', - 'get_or_create.tests.UpdateOrCreateTests.test_manual_primary_key_test', - 'get_or_create.tests.UpdateOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key', - 'test_utils.tests.TestBadSetUpTestData.test_failure_in_setUpTestData_should_rollback_transaction', - 'transaction_hooks.tests.TestConnectionOnCommit.test_discards_hooks_from_rolled_back_savepoint', - 'transaction_hooks.tests.TestConnectionOnCommit.test_inner_savepoint_rolled_back_with_outer', - 'transaction_hooks.tests.TestConnectionOnCommit.test_inner_savepoint_does_not_affect_outer', - # Spanner doesn't support views. - 'inspectdb.tests.InspectDBTransactionalTests.test_include_views', - 'introspection.tests.IntrospectionTests.test_table_names_with_views', - # No sequence for AutoField in Spanner. - 'introspection.tests.IntrospectionTests.test_sequence_list', - # DatabaseIntrospection.get_key_columns() is only required if this - # backend needs it (which it currently doesn't). - 'introspection.tests.IntrospectionTests.test_get_key_columns', - # DatabaseIntrospection.get_relations() isn't implemented: - # https://github.com/orijtech/django-spanner/issues/311 - 'introspection.tests.IntrospectionTests.test_get_relations', - # pyformat parameters not supported on INSERT: - # https://github.com/orijtech/django-spanner/issues/343 - 'backends.tests.BackendTestCase.test_cursor_execute_with_pyformat', - 'backends.tests.BackendTestCase.test_cursor_executemany_with_pyformat', - 'backends.tests.BackendTestCase.test_cursor_executemany_with_pyformat_iterator', - # duplicate table raises GoogleAPICallError rather than DatabaseError: - # https://github.com/orijtech/django-spanner/issues/344 - 'backends.tests.BackendTestCase.test_duplicate_table_error', - 'migrations.test_commands.MigrateTests.test_migrate_fake_initial', - 'migrations.test_commands.MigrateTests.test_migrate_initial_false', - 'migrations.test_executor.ExecutorTests.test_soft_apply', - # Spanner limitation: Cannot change type of column. - 'migrations.test_executor.ExecutorTests.test_alter_id_type_with_fk', - 'schema.tests.SchemaTests.test_alter_auto_field_to_char_field', - 'schema.tests.SchemaTests.test_alter_text_field_to_date_field', - 'schema.tests.SchemaTests.test_alter_text_field_to_datetime_field', - 'schema.tests.SchemaTests.test_alter_text_field_to_time_field', - # Spanner limitation: Cannot rename tables and columns. - 'contenttypes_tests.test_operations.ContentTypeOperationsTests', - 'migrations.test_operations.OperationTests.test_alter_fk_non_fk', - 'migrations.test_operations.OperationTests.test_alter_model_table', - 'migrations.test_operations.OperationTests.test_alter_model_table_m2m', - 'migrations.test_operations.OperationTests.test_rename_field', - 'migrations.test_operations.OperationTests.test_rename_field_reloads_state_on_fk_target_changes', - 'migrations.test_operations.OperationTests.test_rename_m2m_model_after_rename_field', - 'migrations.test_operations.OperationTests.test_rename_m2m_target_model', - 'migrations.test_operations.OperationTests.test_rename_m2m_through_model', - 'migrations.test_operations.OperationTests.test_rename_model', - 'migrations.test_operations.OperationTests.test_rename_model_with_m2m', - 'migrations.test_operations.OperationTests.test_rename_model_with_self_referential_fk', - 'migrations.test_operations.OperationTests.test_rename_model_with_self_referential_m2m', - 'migrations.test_operations.OperationTests.test_rename_model_with_superclass_fk', - 'migrations.test_operations.OperationTests.test_repoint_field_m2m', - 'schema.tests.SchemaTests.test_alter_db_table_case', - 'schema.tests.SchemaTests.test_alter_pk_with_self_referential_field', - 'schema.tests.SchemaTests.test_rename', - 'schema.tests.SchemaTests.test_db_table', - 'schema.tests.SchemaTests.test_m2m_rename_field_in_target_model', - 'schema.tests.SchemaTests.test_m2m_repoint', - 'schema.tests.SchemaTests.test_m2m_repoint_custom', - 'schema.tests.SchemaTests.test_m2m_repoint_inherited', - 'schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references', - 'schema.tests.SchemaTests.test_rename_keep_null_status', - 'schema.tests.SchemaTests.test_rename_referenced_field', - 'schema.tests.SchemaTests.test_rename_table_renames_deferred_sql_references', - 'schema.tests.SchemaTests.test_referenced_field_without_constraint_rename_inside_atomic_block', - 'schema.tests.SchemaTests.test_referenced_table_without_constraint_rename_inside_atomic_block', - 'schema.tests.SchemaTests.test_unique_name_quoting', - # Spanner limitation: Cannot change a field to a primary key. - 'schema.tests.SchemaTests.test_alter_not_unique_field_to_primary_key', - # Spanner limitation: Cannot drop column in primary key. - 'schema.tests.SchemaTests.test_primary_key', - # Spanner limitation: Cannot remove a column from the primary key. - 'schema.tests.SchemaTests.test_alter_int_pk_to_int_unique', - # Spanner limitation: migrations aren't atomic since Spanner doesn't - # support transactions. - 'migrations.test_executor.ExecutorTests.test_atomic_operation_in_non_atomic_migration', - # changing a not null constraint isn't allowed if it affects an index: - # https://github.com/orijtech/django-spanner/issues/378 - 'migrations.test_operations.OperationTests.test_alter_field_with_index', - # parsing INSERT with one inlined value and one placeholder fails: - # https://github.com/orijtech/django-spanner/issues/393 - 'migrations.test_operations.OperationTests.test_run_sql_params', - # This test doesn't flush the database properly: - # https://code.djangoproject.com/ticket/31398 - 'multiple_database.tests.AuthTestCase', - # This test isn't isolated on databases like Spanner that don't - # support transactions: https://code.djangoproject.com/ticket/31413 - 'migrations.test_loader.LoaderTests.test_loading_squashed', - # Probably due to django-spanner setting a default on AutoField: - # https://github.com/googleapis/python-spanner-django/issues/422 - 'model_inheritance_regress.tests.ModelInheritanceTest.test_issue_6755', - # Probably due to django-spanner setting a default on AutoField: - # https://github.com/googleapis/python-spanner-django/issues/424 - 'model_formsets.tests.ModelFormsetTest.test_prevent_change_outer_model_and_create_invalid_data', - 'model_formsets_regress.tests.FormfieldShouldDeleteFormTests.test_no_delete', - 'model_formsets_regress.tests.FormsetTests.test_extraneous_query_is_not_run', - ) - # Kokoro-specific skips. - if os.environ.get('KOKORO_JOB_NAME'): - skip_tests += ( - # os.chmod() doesn't work on Kokoro? - 'file_uploads.tests.DirectoryCreationTests.test_readonly_root', - # Tests that sometimes fail on Kokoro for unknown reasons. - 'contenttypes_tests.test_models.ContentTypesTests.test_cache_not_shared_between_managers', - 'migration_test_data_persistence.tests.MigrationDataNormalPersistenceTestCase.test_persistence', - 'servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections', - ) - - if os.environ.get('SPANNER_EMULATOR_HOST', None): - # Some code isn't yet supported by the Spanner emulator. - skip_tests += ( - # Untyped parameters are not supported: - # https://github.com/GoogleCloudPlatform/cloud-spanner-emulator#features-and-limitations - 'admin_changelist.test_date_hierarchy.DateHierarchyTests.test_bounded_params', # noqa - 'admin_changelist.test_date_hierarchy.DateHierarchyTests.test_bounded_params_with_time_zone', # noqa - 'admin_changelist.test_date_hierarchy.DateHierarchyTests.test_invalid_params', # noqa - 'admin_changelist.tests.ChangeListTests.test_builtin_lookup_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_changelist_view_list_editable_changed_objects_uses_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_computed_list_display_localization', # noqa - 'admin_changelist.tests.ChangeListTests.test_custom_lookup_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_custom_lookup_with_pk_shortcut', # noqa - 'admin_changelist.tests.ChangeListTests.test_custom_paginator', # noqa - 'admin_changelist.tests.ChangeListTests.test_deterministic_order_for_model_ordered_by_its_manager', # noqa - 'admin_changelist.tests.ChangeListTests.test_deterministic_order_for_unordered_model', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_inherited_m2m_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_m2m_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_m2m_to_inherited_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_many_to_many_at_second_level_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_non_unique_related_object_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_non_unique_related_object_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_through_m2m_at_second_level_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_distinct_for_through_m2m_in_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_dynamic_list_display', # noqa - 'admin_changelist.tests.ChangeListTests.test_dynamic_list_display_links', # noqa - 'admin_changelist.tests.ChangeListTests.test_dynamic_list_filter', # noqa - 'admin_changelist.tests.ChangeListTests.test_dynamic_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_get_edited_object_ids', # noqa - 'admin_changelist.tests.ChangeListTests.test_get_list_editable_queryset', # noqa - 'admin_changelist.tests.ChangeListTests.test_get_list_editable_queryset_with_regex_chars_in_prefix', # noqa - 'admin_changelist.tests.ChangeListTests.test_get_select_related_custom_method', # noqa - 'admin_changelist.tests.ChangeListTests.test_multiuser_edit', # noqa - 'admin_changelist.tests.ChangeListTests.test_no_distinct_for_m2m_in_list_filter_without_params', # noqa - 'admin_changelist.tests.ChangeListTests.test_no_list_display_links', # noqa - 'admin_changelist.tests.ChangeListTests.test_object_tools_displayed_no_add_permission', # noqa - 'admin_changelist.tests.ChangeListTests.test_pagination', # noqa - 'admin_changelist.tests.ChangeListTests.test_pagination_page_range', # noqa - 'admin_changelist.tests.ChangeListTests.test_pk_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_editable', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_editable_html', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_empty_changelist_value', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_html', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_set_empty_value_display_in_model_admin', # noqa - 'admin_changelist.tests.ChangeListTests.test_result_list_set_empty_value_display_on_admin_site', # noqa - 'admin_changelist.tests.ChangeListTests.test_select_related_as_empty_tuple', # noqa - 'admin_changelist.tests.ChangeListTests.test_select_related_as_tuple', # noqa - 'admin_changelist.tests.ChangeListTests.test_select_related_preserved', # noqa - 'admin_changelist.tests.ChangeListTests.test_show_all', # noqa - 'admin_changelist.tests.ChangeListTests.test_spanning_relations_with_custom_lookup_in_search_fields', # noqa - 'admin_changelist.tests.ChangeListTests.test_specified_ordering_by_f_expression', # noqa - 'admin_changelist.tests.ChangeListTests.test_specified_ordering_by_f_expression_without_asc_desc', # noqa - 'admin_changelist.tests.ChangeListTests.test_total_ordering_optimization', # noqa - 'admin_changelist.tests.ChangeListTests.test_tuple_list_display', # noqa - 'admin_changelist.tests.GetAdminLogTests.test_no_user', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_add_with_GET_args', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_admin_URLs_no_clash', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_basic_add_GET', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_basic_add_POST', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_post_save_add_redirect', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_post_save_change_redirect', # noqa - 'admin_custom_urls.tests.AdminCustomUrlsTest.test_post_url_continue', # noqa - 'admin_docs.test_middleware.XViewMiddlewareTest.test_callable_object_view', # noqa - 'admin_docs.test_middleware.XViewMiddlewareTest.test_xview_class', # noqa - 'admin_docs.test_middleware.XViewMiddlewareTest.test_xview_func', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_bookmarklets', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_index', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_missing_docutils', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_model_index', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_namespaced_view_detail', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_no_sites_framework', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_template_detail', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_templatefilter_index', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_templatetag_index', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_view_detail', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_view_detail_as_method', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_view_detail_illegal_import', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_view_index', # noqa - 'admin_docs.test_views.AdminDocViewTests.test_view_index_with_method', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_bookmarklets', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_index', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_missing_docutils', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_model_index', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_namespaced_view_detail', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_no_sites_framework', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_template_detail', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_templatefilter_index', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_templatetag_index', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail_as_method', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail_illegal_import', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_index', # noqa - 'admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_index_with_method', # noqa - 'admin_docs.test_views.TestModelDetailView.test_app_not_found', # noqa - 'admin_docs.test_views.TestModelDetailView.test_descriptions_render_correctly', # noqa - 'admin_docs.test_views.TestModelDetailView.test_instance_of_property_methods_are_displayed', # noqa - 'admin_docs.test_views.TestModelDetailView.test_method_data_types', # noqa - 'admin_docs.test_views.TestModelDetailView.test_method_excludes', # noqa - 'admin_docs.test_views.TestModelDetailView.test_methods_with_arguments', # noqa - 'admin_docs.test_views.TestModelDetailView.test_methods_with_arguments_display_arguments', # noqa - 'admin_docs.test_views.TestModelDetailView.test_methods_with_arguments_display_arguments_default_value', # noqa - 'admin_docs.test_views.TestModelDetailView.test_methods_with_multiple_arguments_display_arguments', # noqa - 'admin_docs.test_views.TestModelDetailView.test_model_detail_title', # noqa - 'admin_docs.test_views.TestModelDetailView.test_model_docstring_renders_correctly', # noqa - 'admin_docs.test_views.TestModelDetailView.test_model_not_found', # noqa - 'admin_docs.test_views.TestModelDetailView.test_model_with_many_to_one', # noqa - 'admin_docs.test_views.TestModelDetailView.test_model_with_no_backward_relations_render_only_relevant_fields', # noqa - 'admin_inlines.tests.TestInline.test_callable_lookup', # noqa - 'admin_inlines.tests.TestInline.test_can_delete', # noqa - 'admin_inlines.tests.TestInline.test_create_inlines_on_inherited_model', # noqa - 'admin_inlines.tests.TestInline.test_custom_form_tabular_inline_label', # noqa - 'admin_inlines.tests.TestInline.test_custom_form_tabular_inline_overridden_label', # noqa - 'admin_inlines.tests.TestInline.test_custom_get_extra_form', # noqa - 'admin_inlines.tests.TestInline.test_custom_min_num', # noqa - 'admin_inlines.tests.TestInline.test_custom_pk_shortcut', # noqa - 'admin_inlines.tests.TestInline.test_help_text', # noqa - 'admin_inlines.tests.TestInline.test_inline_editable_pk', # noqa - 'admin_inlines.tests.TestInline.test_inline_nonauto_noneditable_inherited_pk', # noqa - 'admin_inlines.tests.TestInline.test_inline_nonauto_noneditable_pk', # noqa - 'admin_inlines.tests.TestInline.test_inline_primary', # noqa - 'admin_inlines.tests.TestInline.test_inlines_show_change_link_registered', # noqa - 'admin_inlines.tests.TestInline.test_inlines_show_change_link_unregistered', # noqa - 'admin_inlines.tests.TestInline.test_localize_pk_shortcut', # noqa - 'admin_inlines.tests.TestInline.test_many_to_many_inlines', # noqa - 'admin_inlines.tests.TestInline.test_min_num', # noqa - 'admin_inlines.tests.TestInline.test_no_parent_callable_lookup', # noqa - 'admin_inlines.tests.TestInline.test_non_related_name_inline', # noqa - 'admin_inlines.tests.TestInline.test_noneditable_inline_has_field_inputs', # noqa - 'admin_inlines.tests.TestInline.test_readonly_stacked_inline_label', # noqa - 'admin_inlines.tests.TestInline.test_stacked_inline_edit_form_contains_has_original_class', # noqa - 'admin_inlines.tests.TestInline.test_tabular_inline_column_css_class', # noqa - 'admin_inlines.tests.TestInline.test_tabular_inline_show_change_link_false_registered', # noqa - 'admin_inlines.tests.TestInline.test_tabular_model_form_meta_readonly_field', # noqa - 'admin_inlines.tests.TestInline.test_tabular_non_field_errors', # noqa - 'admin_inlines.tests.TestInlineMedia.test_all_inline_media', # noqa - 'admin_inlines.tests.TestInlineMedia.test_inline_media_only_base', # noqa - 'admin_inlines.tests.TestInlineMedia.test_inline_media_only_inline', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_add_fk_add_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_add_fk_noperm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_add_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_noperm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_view_only_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_add_change_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_add_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_all_perms', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_change_del_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_change_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_noperm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_add_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_change_perm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_noperm', # noqa - 'admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_view_only_perm', # noqa - 'admin_inlines.tests.TestInlineProtectedOnDelete.test_deleting_inline_with_protected_delete_does_not_validate', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_add_url_not_allowed', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_extra_inlines_are_not_shown', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_get_to_change_url_is_allowed', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_inline_delete_buttons_are_not_shown', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_inlines_are_rendered_as_read_only', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_main_model_is_rendered_as_read_only', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_post_to_change_url_not_allowed', # noqa - 'admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_submit_line_shows_only_close_button', # noqa - 'admin_ordering.tests.TestAdminOrdering.test_dynamic_ordering', # noqa - 'aggregation.tests.AggregateTestCase.test_add_implementation', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregate_alias', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregate_annotation', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregate_in_order_by', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregate_multi_join', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregate_over_complex_annotation', # noqa - 'aggregation.tests.AggregateTestCase.test_aggregation_expressions', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_defer', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_defer_select_related', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_m2m', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_ordering', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_over_annotate', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_values', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_values_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_annotate_values_list', # noqa - 'aggregation.tests.AggregateTestCase.test_annotated_aggregate_over_annotated_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_annotation_expressions', # noqa - 'aggregation.tests.AggregateTestCase.test_arguments_must_be_expressions', # noqa - 'aggregation.tests.AggregateTestCase.test_avg_decimal_field', # noqa - 'aggregation.tests.AggregateTestCase.test_avg_duration_field', # noqa - 'aggregation.tests.AggregateTestCase.test_backwards_m2m_annotate', # noqa - 'aggregation.tests.AggregateTestCase.test_combine_different_types', # noqa - 'aggregation.tests.AggregateTestCase.test_complex_aggregations_require_kwarg', # noqa - 'aggregation.tests.AggregateTestCase.test_complex_values_aggregation', # noqa - 'aggregation.tests.AggregateTestCase.test_count', # noqa - 'aggregation.tests.AggregateTestCase.test_count_distinct_expression', # noqa - 'aggregation.tests.AggregateTestCase.test_count_star', # noqa - 'aggregation.tests.AggregateTestCase.test_dates_with_aggregation', # noqa - 'aggregation.tests.AggregateTestCase.test_empty_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_even_more_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_expression_on_aggregation', # noqa - 'aggregation.tests.AggregateTestCase.test_filter_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_fkey_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_grouped_annotation_in_group_by', # noqa - 'aggregation.tests.AggregateTestCase.test_missing_output_field_raises_error', # noqa - 'aggregation.tests.AggregateTestCase.test_more_aggregation', # noqa - 'aggregation.tests.AggregateTestCase.test_multi_arg_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_multiple_aggregates', # noqa - 'aggregation.tests.AggregateTestCase.test_non_grouped_annotation_not_in_group_by', # noqa - 'aggregation.tests.AggregateTestCase.test_nonaggregate_aggregation_throws', # noqa - 'aggregation.tests.AggregateTestCase.test_nonfield_annotation', # noqa - 'aggregation.tests.AggregateTestCase.test_order_of_precedence', # noqa - 'aggregation.tests.AggregateTestCase.test_reverse_fkey_annotate', # noqa - 'aggregation.tests.AggregateTestCase.test_single_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_sum_distinct_aggregate', # noqa - 'aggregation.tests.AggregateTestCase.test_sum_duration_field', # noqa - 'aggregation.tests.AggregateTestCase.test_ticket11881', # noqa - 'aggregation.tests.AggregateTestCase.test_ticket12886', # noqa - 'aggregation.tests.AggregateTestCase.test_ticket17424', # noqa - 'aggregation.tests.AggregateTestCase.test_values_aggregation', # noqa - 'aggregation.tests.AggregateTestCase.test_values_annotation_with_expression', # noqa - 'aggregation_regress.tests.JoinPromotionTests.test_ticket_21150', # noqa - 'aggregation_regress.tests.SelfReferentialFKTests.test_ticket_24748', # noqa - 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions', # noqa - 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions_can_ref_other_functions', # noqa - 'auth_tests.test_auth_backends.AllowAllUsersModelBackendTest.test_authenticate', # noqa - 'auth_tests.test_auth_backends.AllowAllUsersModelBackendTest.test_get_user', # noqa - 'auth_tests.test_auth_backends.AuthenticateTests.test_skips_backends_without_arguments', # noqa - 'auth_tests.test_auth_backends.AuthenticateTests.test_type_error_raised', # noqa - 'auth_tests.test_auth_backends.ChangedBackendSettingsTest.test_changed_backend_settings', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_anonymous_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_authentication_timing', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_custom_perms', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_get_all_superuser_permissions', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_has_no_object_perm', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_inactive_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.CustomUserModelBackendAuthenticateTest.test_authenticate', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_anonymous_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_authentication_timing', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_custom_perms', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_get_all_superuser_permissions', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_has_no_object_perm', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_inactive_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.ImportedBackendTests.test_backend_path', # noqa - 'auth_tests.test_auth_backends.ImproperlyConfiguredUserModelTest.test_does_not_shadow_exception', # noqa - 'auth_tests.test_auth_backends.InActiveUserBackendTest.test_has_module_perms', # noqa - 'auth_tests.test_auth_backends.InActiveUserBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_anonymous_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_authenticate_inactive', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_authenticate_user_without_is_active_field', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_authentication_timing', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_custom_perms', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_get_all_superuser_permissions', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_has_no_object_perm', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.ModelBackendTest.test_inactive_has_no_permissions', # noqa - 'auth_tests.test_auth_backends.NoBackendsTest.test_raises_exception', # noqa - 'auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_authenticates', # noqa - 'auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_has_perm_denied', # noqa - 'auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_permission_denied', # noqa - 'auth_tests.test_auth_backends.RowlevelBackendTest.test_get_all_permissions', # noqa - 'auth_tests.test_auth_backends.RowlevelBackendTest.test_get_group_permissions', # noqa - 'auth_tests.test_auth_backends.RowlevelBackendTest.test_has_perm', # noqa - 'auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_with_explicit_backends', # noqa - 'auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_without_authenticate_multiple_backends', # noqa - 'auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_without_authenticate_single_backend', # noqa - 'auth_tests.test_auth_backends.SelectingBackendTests.test_non_string_backend', # noqa - 'auth_tests.test_auth_backends.UUIDUserTests.test_login', # noqa - 'auth_tests.test_basic.BasicTestCase.test_superuser', # noqa - 'auth_tests.test_basic.BasicTestCase.test_unicode_username', # noqa - 'auth_tests.test_basic.BasicTestCase.test_user', # noqa - 'auth_tests.test_basic.BasicTestCase.test_user_no_email', # noqa - 'auth_tests.test_basic.TestGetUser.test_get_user', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_message_attrs', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_perm_in_perms_attrs', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_perms_attrs', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_session_is_accessed', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_session_not_accessed', # noqa - 'auth_tests.test_context_processors.AuthContextProcessorTests.test_user_attrs', # noqa - 'auth_tests.test_decorators.LoginRequiredTestCase.testCallable', # noqa - 'auth_tests.test_decorators.LoginRequiredTestCase.testLoginRequired', # noqa - 'auth_tests.test_decorators.LoginRequiredTestCase.testLoginRequiredNextUrl', # noqa - 'auth_tests.test_decorators.LoginRequiredTestCase.testView', # noqa - 'auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_many_permissions_in_set_pass', # noqa - 'auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_many_permissions_pass', # noqa - 'auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_permissioned_denied_exception_raised', # noqa - 'auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_permissioned_denied_redirect', # noqa - 'auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_single_permission_pass', # noqa - 'auth_tests.test_forms.AdminPasswordChangeFormTest.test_missing_passwords', # noqa - 'auth_tests.test_forms.AdminPasswordChangeFormTest.test_non_matching_passwords', # noqa - 'auth_tests.test_forms.AdminPasswordChangeFormTest.test_one_password', # noqa - 'auth_tests.test_forms.AdminPasswordChangeFormTest.test_password_whitespace_not_stripped', # noqa - 'auth_tests.test_forms.AdminPasswordChangeFormTest.test_success', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_custom_login_allowed_policy', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_get_invalid_login_error', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_inactive_user', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_inactive_user_i18n', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_inactive_user_incorrect_password', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_integer_username', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_invalid_username', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_login_failed', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_password_whitespace_not_stripped', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_success', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_unicode_username', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_username_field_label', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_username_field_label_empty_string', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_username_field_label_not_set', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_username_field_max_length_defaults_to_254', # noqa - 'auth_tests.test_forms.AuthenticationFormTest.test_username_field_max_length_matches_user_model', # noqa - 'auth_tests.test_forms.PasswordChangeFormTest.test_field_order', # noqa - 'auth_tests.test_forms.PasswordChangeFormTest.test_incorrect_password', # noqa - 'auth_tests.test_forms.PasswordChangeFormTest.test_password_verification', # noqa - 'auth_tests.test_forms.PasswordChangeFormTest.test_password_whitespace_not_stripped', # noqa - 'auth_tests.test_forms.PasswordChangeFormTest.test_success', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_cleaned_data', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_custom_email_constructor', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_custom_email_field', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_custom_email_subject', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_inactive_user', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_invalid_email', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_nonexistent_email', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_preserve_username_case', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_save_html_email_template_name', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_save_plaintext_email', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_unusable_password', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_user_email_domain_unicode_collision', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_user_email_domain_unicode_collision_nonexistent', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_user_email_unicode_collision', # noqa - 'auth_tests.test_forms.PasswordResetFormTest.test_user_email_unicode_collision_nonexistent', # noqa - 'auth_tests.test_forms.SetPasswordFormTest.test_help_text_translation', # noqa - 'auth_tests.test_forms.SetPasswordFormTest.test_password_verification', # noqa - 'auth_tests.test_forms.SetPasswordFormTest.test_password_whitespace_not_stripped', # noqa - 'auth_tests.test_forms.SetPasswordFormTest.test_success', # noqa - 'auth_tests.test_forms.SetPasswordFormTest.test_validates_password', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_14242', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_17944_empty_password', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_17944_unknown_password_algorithm', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_17944_unmanageable_password', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_19133', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_bug_19349_bound_password_field', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_custom_form', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_password_excluded', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_unusable_password', # noqa - 'auth_tests.test_forms.UserChangeFormTest.test_username_validity', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_both_passwords', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_custom_form', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_custom_form_hidden_username_field', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_custom_form_with_different_username_field', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_duplicate_normalized_unicode', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_invalid_data', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_normalize_username', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_password_help_text', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_password_verification', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_password_whitespace_not_stripped', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_success', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_unicode_username', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_user_already_exists', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_user_create_form_validates_password_with_all_data', # noqa - 'auth_tests.test_forms.UserCreationFormTest.test_validates_password', # noqa - 'auth_tests.test_handlers.ModWsgiHandlerTestCase.test_check_password', # noqa - 'auth_tests.test_handlers.ModWsgiHandlerTestCase.test_check_password_custom_user', # noqa - 'auth_tests.test_handlers.ModWsgiHandlerTestCase.test_groups_for_user', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_get_pass', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_get_pass_no_input', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_nonexistent_username', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_password_validation', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_system_username', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_changepassword_command_changes_joes_password', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_changepassword_command_works_with_nonascii_output', # noqa - 'auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_max_tries_exits_1', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_basic_usage', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_default_username', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_email_in_username', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username_non_interactive', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username_provided_via_option_and_interactive', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_fields_with_fk', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_fields_with_fk_interactive', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_invalid_username', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_non_ascii_verbose_name', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_passing_stdin', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_password_validation', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_password_validation_bypass', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_swappable_user', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_swappable_user_username_non_unique', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validate_password_against_required_fields', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validate_password_against_username', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validation_blank_password_entered', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validation_mismatched_passwords', # noqa - 'auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_verbosity_zero', # noqa - 'auth_tests.test_management.GetDefaultUsernameTestCase.test_existing', # noqa - 'auth_tests.test_management.MultiDBChangepasswordManagementCommandTestCase.test_that_changepassword_command_with_database_option_uses_given_db', # noqa - 'auth_tests.test_management.MultiDBCreatesuperuserTestCase.test_createsuperuser_command_with_database_option', # noqa - 'auth_tests.test_middleware.TestAuthenticationMiddleware.test_changed_password_invalidates_session', # noqa - 'auth_tests.test_middleware.TestAuthenticationMiddleware.test_no_password_change_doesnt_invalidate_session', # noqa - 'auth_tests.test_migrations.ProxyModelWithDifferentAppLabelTests.test_user_has_now_proxy_model_permissions', # noqa - 'auth_tests.test_migrations.ProxyModelWithDifferentAppLabelTests.test_user_keeps_same_permissions_after_migrating_backward', # noqa - 'auth_tests.test_migrations.ProxyModelWithSameAppLabelTests.test_user_keeps_same_permissions_after_migrating_backward', # noqa - 'auth_tests.test_migrations.ProxyModelWithSameAppLabelTests.test_user_still_has_proxy_model_permissions', # noqa - 'auth_tests.test_mixins.AccessMixinTests.test_access_mixin_permission_denied_response', # noqa - 'auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_missing_permission', # noqa - 'auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_not_logged_in', # noqa - 'auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_success', # noqa - 'auth_tests.test_mixins.LoginRequiredMixinTests.test_login_required', # noqa - 'auth_tests.test_mixins.PermissionsRequiredMixinTests.test_many_permissions_pass', # noqa - 'auth_tests.test_mixins.PermissionsRequiredMixinTests.test_permissioned_denied_exception_raised', # noqa - 'auth_tests.test_mixins.PermissionsRequiredMixinTests.test_permissioned_denied_redirect', # noqa - 'auth_tests.test_mixins.PermissionsRequiredMixinTests.test_single_permission_pass', # noqa - 'auth_tests.test_models.AbstractUserTestCase.test_check_password_upgrade', # noqa - 'auth_tests.test_models.AbstractUserTestCase.test_last_login_default', # noqa - 'auth_tests.test_models.AbstractUserTestCase.test_user_double_save', # noqa - 'auth_tests.test_models.IsActiveTestCase.test_builtin_user_isactive', # noqa - 'auth_tests.test_models.IsActiveTestCase.test_is_active_field_default', # noqa - 'auth_tests.test_models.NaturalKeysTestCase.test_user_natural_key', # noqa - 'auth_tests.test_models.TestCreateSuperUserSignals.test_create_superuser', # noqa - 'auth_tests.test_models.TestCreateSuperUserSignals.test_create_user', # noqa - 'auth_tests.test_models.UserManagerTestCase.test_create_user', # noqa - 'auth_tests.test_models.UserManagerTestCase.test_create_user_is_staff', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_known_user', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_last_login', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_unknown_user', # noqa - 'auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_known_user', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_last_login', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_unknown_user', # noqa - 'auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_known_user', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_last_login', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_unknown_user', # noqa - 'auth_tests.test_remote_user.PersistentRemoteUserTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_known_user', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_last_login', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_unknown_user', # noqa - 'auth_tests.test_remote_user.RemoteUserCustomTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user.RemoteUserNoCreateTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.RemoteUserNoCreateTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.RemoteUserNoCreateTest.test_known_user', # noqa - 'auth_tests.test_remote_user.RemoteUserNoCreateTest.test_last_login', # noqa - 'auth_tests.test_remote_user.RemoteUserNoCreateTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_header_disappears', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_inactive_user', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_known_user', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_last_login', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_unknown_user', # noqa - 'auth_tests.test_remote_user.RemoteUserTest.test_user_switch_forces_new_login', # noqa - 'auth_tests.test_remote_user_deprecation.RemoteUserCustomTest.test_configure_user_deprecation_warning', # noqa - 'auth_tests.test_signals.SignalTestCase.test_failed_login_without_request', # noqa - 'auth_tests.test_signals.SignalTestCase.test_login', # noqa - 'auth_tests.test_signals.SignalTestCase.test_login_with_custom_user_without_last_login_field', # noqa - 'auth_tests.test_signals.SignalTestCase.test_logout', # noqa - 'auth_tests.test_signals.SignalTestCase.test_logout_anonymous', # noqa - 'auth_tests.test_signals.SignalTestCase.test_update_last_login', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordChangeDoneView', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetChangeView', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetCompleteView', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetConfirmView_invalid_token', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetConfirmView_valid_token', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetDoneView', # noqa - 'auth_tests.test_templates.AuthTemplateTests.test_PasswordResetView', # noqa - 'auth_tests.test_tokens.TokenGeneratorTest.test_10265', # noqa - 'auth_tests.test_tokens.TokenGeneratorTest.test_check_token_with_nonexistent_token_and_user', # noqa - 'auth_tests.test_tokens.TokenGeneratorTest.test_make_token', # noqa - 'auth_tests.test_tokens.TokenGeneratorTest.test_timeout', # noqa - 'auth_tests.test_tokens.TokenGeneratorTest.test_token_with_different_secret', # noqa - 'auth_tests.test_validators.UserAttributeSimilarityValidatorTest.test_validate', # noqa - 'auth_tests.test_views.AuthViewNamedURLTests.test_named_urls', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_done_fails', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_done_succeeds', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_fails_with_invalid_old_password', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_fails_with_mismatched_passwords', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_custom', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_custom_named', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_default', # noqa - 'auth_tests.test_views.ChangePasswordTest.test_password_change_succeeds', # noqa - 'auth_tests.test_views.ChangelistTests.test_changelist_disallows_password_lookups', # noqa - 'auth_tests.test_views.ChangelistTests.test_password_change_bad_url', # noqa - 'auth_tests.test_views.ChangelistTests.test_user_change_different_user_password', # noqa - 'auth_tests.test_views.ChangelistTests.test_user_change_email', # noqa - 'auth_tests.test_views.ChangelistTests.test_user_change_password', # noqa - 'auth_tests.test_views.ChangelistTests.test_user_change_password_passes_user_to_has_change_permission', # noqa - 'auth_tests.test_views.ChangelistTests.test_user_not_change', # noqa - 'auth_tests.test_views.ChangelistTests.test_view_user_password_is_readonly', # noqa - 'auth_tests.test_views.CustomUserPasswordResetTest.test_confirm_valid_custom_user', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_default', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_guest', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_permission_required_logged_in', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_permission_required_not_logged_in', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_loop', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_param', # noqa - 'auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_url', # noqa - 'auth_tests.test_views.LoginRedirectUrlTest.test_custom', # noqa - 'auth_tests.test_views.LoginRedirectUrlTest.test_default', # noqa - 'auth_tests.test_views.LoginRedirectUrlTest.test_named', # noqa - 'auth_tests.test_views.LoginRedirectUrlTest.test_remote', # noqa - 'auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_safe_host', # noqa - 'auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_same_host', # noqa - 'auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_unsafe_host', # noqa - 'auth_tests.test_views.LoginTest.test_current_site_in_context_after_login', # noqa - 'auth_tests.test_views.LoginTest.test_login_csrf_rotate', # noqa - 'auth_tests.test_views.LoginTest.test_login_form_contains_request', # noqa - 'auth_tests.test_views.LoginTest.test_login_session_without_hash_session_key', # noqa - 'auth_tests.test_views.LoginTest.test_security_check', # noqa - 'auth_tests.test_views.LoginTest.test_security_check_https', # noqa - 'auth_tests.test_views.LoginTest.test_session_key_flushed_on_login', # noqa - 'auth_tests.test_views.LoginTest.test_session_key_flushed_on_login_after_password_change', # noqa - 'auth_tests.test_views.LoginURLSettings.test_https_login_url', # noqa - 'auth_tests.test_views.LoginURLSettings.test_lazy_login_url', # noqa - 'auth_tests.test_views.LoginURLSettings.test_login_url_with_querystring', # noqa - 'auth_tests.test_views.LoginURLSettings.test_named_login_url', # noqa - 'auth_tests.test_views.LoginURLSettings.test_remote_login_url', # noqa - 'auth_tests.test_views.LoginURLSettings.test_remote_login_url_with_next_querystring', # noqa - 'auth_tests.test_views.LoginURLSettings.test_standard_login_url', # noqa - 'auth_tests.test_views.LogoutTest.test_14377', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_default', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_doesnt_cache', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_preserve_language', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_redirect_url_named_setting', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_redirect_url_setting', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_custom_redirect_argument', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_named_redirect', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_next_page_specified', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_overridden_redirect_url', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_post', # noqa - 'auth_tests.test_views.LogoutTest.test_logout_with_redirect_argument', # noqa - 'auth_tests.test_views.LogoutTest.test_security_check', # noqa - 'auth_tests.test_views.LogoutTest.test_security_check_https', # noqa - 'auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_safe_host', # noqa - 'auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_same_host', # noqa - 'auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_unsafe_host', # noqa - 'auth_tests.test_views.LogoutThenLoginTests.test_default_logout_then_login', # noqa - 'auth_tests.test_views.LogoutThenLoginTests.test_logout_then_login_with_custom_login', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_complete', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_different_passwords', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_display_user_from_form', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_invalid', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_invalid_hash', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_invalid_post', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_invalid_user', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_link_redirects_to_set_password_page', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset_already_logged_in', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset_custom_backend', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_overflow_user', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_redirect_custom', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_redirect_custom_named', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_redirect_default', # noqa - 'auth_tests.test_views.PasswordResetTest.test_confirm_valid', # noqa - 'auth_tests.test_views.PasswordResetTest.test_email_found', # noqa - 'auth_tests.test_views.PasswordResetTest.test_email_found_custom_from', # noqa - 'auth_tests.test_views.PasswordResetTest.test_email_not_found', # noqa - 'auth_tests.test_views.PasswordResetTest.test_extra_email_context', # noqa - 'auth_tests.test_views.PasswordResetTest.test_html_mail_template', # noqa - 'auth_tests.test_views.PasswordResetTest.test_invalid_link_if_going_directly_to_the_final_reset_password_url', # noqa - 'auth_tests.test_views.PasswordResetTest.test_poisoned_http_host', # noqa - 'auth_tests.test_views.PasswordResetTest.test_poisoned_http_host_admin_site', # noqa - 'auth_tests.test_views.PasswordResetTest.test_reset_custom_redirect', # noqa - 'auth_tests.test_views.PasswordResetTest.test_reset_custom_redirect_named', # noqa - 'auth_tests.test_views.PasswordResetTest.test_reset_redirect_default', # noqa - 'auth_tests.test_views.RedirectToLoginTests.test_redirect_to_login_with_lazy', # noqa - 'auth_tests.test_views.RedirectToLoginTests.test_redirect_to_login_with_lazy_and_unicode', # noqa - 'auth_tests.test_views.SessionAuthenticationTests.test_user_password_change_updates_session', # noqa - 'auth_tests.test_views.UUIDUserPasswordResetTest.test_confirm_invalid_uuid', # noqa - 'auth_tests.test_views.UUIDUserPasswordResetTest.test_confirm_valid_custom_user', # noqa - 'auth_tests.test_views.UUIDUserTests.test_admin_password_change', # noqa - 'backends.tests.FkConstraintsTests.test_disable_constraint_checks_context_manager', # noqa - 'backends.tests.FkConstraintsTests.test_disable_constraint_checks_manually', # noqa - 'backends.tests.FkConstraintsTests.test_integrity_checks_on_creation', # noqa - 'backends.tests.FkConstraintsTests.test_integrity_checks_on_update', # noqa - 'basic.tests.ModelTest.test_ticket_20278', - 'basic.tests.ModelRefreshTests.test_lookup_in_fields', - 'basic.tests.ModelRefreshTests.test_prefetched_cache_cleared', - 'basic.tests.ModelRefreshTests.test_lookup_in_fields', - 'basic.tests.ModelRefreshTests.test_prefetched_cache_cleared', - 'basic.tests.ModelRefreshTests.test_refresh_fk', - 'basic.tests.ModelRefreshTests.test_refresh_fk_on_delete_set_null', - 'basic.tests.ModelRefreshTests.test_refresh_null_fk', - 'basic.tests.ModelRefreshTests.test_unknown_kwarg', - 'bulk_create.tests.BulkCreateTests.test_bulk_insert_nullable_fields', # noqa - 'custom_pk.tests.CustomPKTests.test_required_pk', # noqa - 'custom_pk.tests.CustomPKTests.test_unique_pk', # noqa - 'datatypes.tests.DataTypesTestCase.test_boolean_type', # noqa - 'datatypes.tests.DataTypesTestCase.test_date_type', # noqa - 'datatypes.tests.DataTypesTestCase.test_textfields_str', # noqa - 'datatypes.tests.DataTypesTestCase.test_time_field', # noqa - 'datatypes.tests.DataTypesTestCase.test_year_boundaries', # noqa - 'dates.tests.DatesTests.test_related_model_traverse', # noqa - 'datetimes.tests.DateTimesTests.test_datetimes_has_lazy_iterator', # noqa - 'datetimes.tests.DateTimesTests.test_datetimes_returns_available_dates_for_given_scope_and_given_field', # noqa - 'datetimes.tests.DateTimesTests.test_related_model_traverse', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_date', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_date_group_by', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_time', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_field', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_python', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_date', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_datetime', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_from_value', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_to_char_field_with_max_length', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_to_char_field_without_max_length', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_to_integer', # noqa - 'db_functions.comparison.test_cast.CastTests.test_cast_to_text_field', # noqa - 'db_functions.comparison.test_coalesce.CoalesceTests.test_basic', # noqa - 'db_functions.comparison.test_coalesce.CoalesceTests.test_mixed_values', # noqa - 'db_functions.comparison.test_coalesce.CoalesceTests.test_ordering', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_all_null', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_basic', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_coalesce_workaround', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_propagates_null', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_related_field', # noqa - 'db_functions.comparison.test_greatest.GreatestTests.test_update', # noqa - 'db_functions.comparison.test_least.LeastTests.test_all_null', # noqa - 'db_functions.comparison.test_least.LeastTests.test_basic', # noqa - 'db_functions.comparison.test_least.LeastTests.test_coalesce_workaround', # noqa - 'db_functions.comparison.test_least.LeastTests.test_propagates_null', # noqa - 'db_functions.comparison.test_least.LeastTests.test_related_field', # noqa - 'db_functions.comparison.test_least.LeastTests.test_update', # noqa - 'db_functions.comparison.test_nullif.NullIfTests.test_basic', # noqa - 'db_functions.comparison.test_nullif.NullIfTests.test_null_argument', # noqa - 'db_functions.comparison.test_nullif.NullIfTests.test_too_few_args', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_date_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_subquery_with_parameters', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_time_func', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_time_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_extract_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_date_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_none', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_subquery_with_parameters', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_time_func', # noqa - 'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_time_none', # noqa - 'db_functions.datetime.test_now.NowTests.test_basic', # noqa - 'db_functions.math.test_abs.AbsTests.test_null', # noqa - 'db_functions.math.test_acos.ACosTests.test_null', # noqa - 'db_functions.math.test_asin.ASinTests.test_null', # noqa - 'db_functions.math.test_atan.ATanTests.test_null', # noqa - 'db_functions.math.test_atan2.ATan2Tests.test_null', # noqa - 'db_functions.math.test_ceil.CeilTests.test_decimal', # noqa - 'db_functions.math.test_ceil.CeilTests.test_float', # noqa - 'db_functions.math.test_ceil.CeilTests.test_integer', # noqa - 'db_functions.math.test_ceil.CeilTests.test_null', # noqa - 'db_functions.math.test_ceil.CeilTests.test_transform', # noqa - 'db_functions.math.test_cos.CosTests.test_null', # noqa - 'db_functions.math.test_cot.CotTests.test_null', # noqa - 'db_functions.math.test_degrees.DegreesTests.test_null', # noqa - 'db_functions.math.test_exp.ExpTests.test_null', # noqa - 'db_functions.math.test_floor.FloorTests.test_null', # noqa - 'db_functions.math.test_ln.LnTests.test_null', # noqa - 'db_functions.math.test_log.LogTests.test_null', # noqa - 'db_functions.math.test_mod.ModTests.test_null', # noqa - 'db_functions.math.test_power.PowerTests.test_decimal', # noqa - 'db_functions.math.test_power.PowerTests.test_float', # noqa - 'db_functions.math.test_power.PowerTests.test_integer', # noqa - 'db_functions.math.test_power.PowerTests.test_null', # noqa - 'db_functions.math.test_radians.RadiansTests.test_null', # noqa - 'db_functions.math.test_round.RoundTests.test_null', # noqa - 'db_functions.math.test_sin.SinTests.test_null', # noqa - 'db_functions.math.test_sqrt.SqrtTests.test_null', # noqa - 'db_functions.math.test_tan.TanTests.test_null', # noqa - 'db_functions.tests.FunctionTests.test_func_transform_bilateral', # noqa - 'db_functions.tests.FunctionTests.test_func_transform_bilateral_multivalue', # noqa - 'db_functions.tests.FunctionTests.test_function_as_filter', # noqa - 'db_functions.tests.FunctionTests.test_nested_function_ordering', # noqa - 'db_functions.text.test_chr.ChrTests.test_basic', # noqa - 'db_functions.text.test_chr.ChrTests.test_non_ascii', # noqa - 'db_functions.text.test_chr.ChrTests.test_transform', # noqa - 'db_functions.text.test_concat.ConcatTests.test_basic', # noqa - 'db_functions.text.test_concat.ConcatTests.test_many', # noqa - 'db_functions.text.test_concat.ConcatTests.test_mixed_char_text', # noqa - 'db_functions.text.test_left.LeftTests.test_basic', # noqa - 'db_functions.text.test_left.LeftTests.test_expressions', # noqa - 'db_functions.text.test_left.LeftTests.test_invalid_length', # noqa - 'db_functions.text.test_length.LengthTests.test_basic', # noqa - 'db_functions.text.test_length.LengthTests.test_ordering', # noqa - 'db_functions.text.test_length.LengthTests.test_transform', # noqa - 'db_functions.text.test_lower.LowerTests.test_basic', # noqa - 'db_functions.text.test_lower.LowerTests.test_transform', # noqa - 'db_functions.text.test_ord.OrdTests.test_basic', # noqa - 'db_functions.text.test_ord.OrdTests.test_transform', # noqa - 'db_functions.text.test_pad.PadTests.test_combined_with_length', # noqa - 'db_functions.text.test_pad.PadTests.test_pad', # noqa - 'db_functions.text.test_repeat.RepeatTests.test_basic', # noqa - 'db_functions.text.test_replace.ReplaceTests.test_case_sensitive', # noqa - 'db_functions.text.test_replace.ReplaceTests.test_replace_expression', # noqa - 'db_functions.text.test_replace.ReplaceTests.test_replace_with_default_arg', # noqa - 'db_functions.text.test_replace.ReplaceTests.test_replace_with_empty_string', # noqa - 'db_functions.text.test_replace.ReplaceTests.test_update', # noqa - 'db_functions.text.test_reverse.ReverseTests.test_basic', # noqa - 'db_functions.text.test_reverse.ReverseTests.test_expressions', # noqa - 'db_functions.text.test_reverse.ReverseTests.test_null', # noqa - 'db_functions.text.test_reverse.ReverseTests.test_transform', # noqa - 'db_functions.text.test_right.RightTests.test_basic', # noqa - 'db_functions.text.test_right.RightTests.test_expressions', # noqa - 'db_functions.text.test_right.RightTests.test_invalid_length', # noqa - 'db_functions.text.test_strindex.StrIndexTests.test_annotate_charfield', # noqa - 'db_functions.text.test_strindex.StrIndexTests.test_annotate_textfield', # noqa - 'db_functions.text.test_strindex.StrIndexTests.test_filtering', # noqa - 'db_functions.text.test_strindex.StrIndexTests.test_order_by', # noqa - 'db_functions.text.test_strindex.StrIndexTests.test_unicode_values', # noqa - 'db_functions.text.test_substr.SubstrTests.test_basic', # noqa - 'db_functions.text.test_substr.SubstrTests.test_expressions', # noqa - 'db_functions.text.test_substr.SubstrTests.test_start', # noqa - 'db_functions.text.test_trim.TrimTests.test_trim', # noqa - 'db_functions.text.test_trim.TrimTests.test_trim_transform', # noqa - 'db_functions.text.test_upper.UpperTests.test_basic', # noqa - 'db_functions.text.test_upper.UpperTests.test_transform', # noqa - 'defer_regress.tests.DeferAnnotateSelectRelatedTest.test_defer_annotate_select_related', # noqa - 'delete_regress.tests.DeleteCascadeTransactionTests.test_inheritance', # noqa - 'expressions.test_queryset_values.ValuesExpressionsTests.test_chained_values_with_expression', # noqa - 'expressions.test_queryset_values.ValuesExpressionsTests.test_values_expression', # noqa - 'expressions.test_queryset_values.ValuesExpressionsTests.test_values_expression_group_by', # noqa - 'expressions.test_queryset_values.ValuesExpressionsTests.test_values_list_expression', # noqa - 'expressions.test_queryset_values.ValuesExpressionsTests.test_values_list_expression_flat', # noqa - 'expressions.tests.BasicExpressionsTests.test_annotate_values_aggregate', # noqa - 'expressions.tests.BasicExpressionsTests.test_annotate_values_filter', # noqa - 'expressions.tests.BasicExpressionsTests.test_annotations_within_subquery', # noqa - 'expressions.tests.BasicExpressionsTests.test_arithmetic', # noqa - 'expressions.tests.BasicExpressionsTests.test_exist_single_field_output_field', # noqa - 'expressions.tests.BasicExpressionsTests.test_explicit_output_field', # noqa - 'expressions.tests.BasicExpressionsTests.test_filter_inter_attribute', # noqa - 'expressions.tests.BasicExpressionsTests.test_filter_with_join', # noqa - 'expressions.tests.BasicExpressionsTests.test_in_subquery', # noqa - 'expressions.tests.BasicExpressionsTests.test_incorrect_field_in_F_expression', # noqa - 'expressions.tests.BasicExpressionsTests.test_incorrect_joined_field_in_F_expression', # noqa - 'expressions.tests.BasicExpressionsTests.test_nested_subquery', # noqa - 'expressions.tests.BasicExpressionsTests.test_nested_subquery_outer_ref_2', # noqa - 'expressions.tests.BasicExpressionsTests.test_nested_subquery_outer_ref_with_autofield', # noqa - 'expressions.tests.BasicExpressionsTests.test_new_object_create', # noqa - 'expressions.tests.BasicExpressionsTests.test_new_object_save', # noqa - 'expressions.tests.BasicExpressionsTests.test_object_create_with_aggregate', # noqa - 'expressions.tests.BasicExpressionsTests.test_object_update', # noqa - 'expressions.tests.BasicExpressionsTests.test_object_update_fk', # noqa - 'expressions.tests.BasicExpressionsTests.test_object_update_unsaved_objects', # noqa - 'expressions.tests.BasicExpressionsTests.test_order_by_exists', # noqa - 'expressions.tests.BasicExpressionsTests.test_order_of_operations', # noqa - 'expressions.tests.BasicExpressionsTests.test_outerref', # noqa - 'expressions.tests.BasicExpressionsTests.test_outerref_with_operator', # noqa - 'expressions.tests.BasicExpressionsTests.test_parenthesis_priority', # noqa - 'expressions.tests.BasicExpressionsTests.test_pickle_expression', # noqa - 'expressions.tests.BasicExpressionsTests.test_subquery', # noqa - 'expressions.tests.BasicExpressionsTests.test_subquery_filter_by_aggregate', # noqa - 'expressions.tests.BasicExpressionsTests.test_subquery_references_joined_table_twice', # noqa - 'expressions.tests.BasicExpressionsTests.test_ticket_11722_iexact_lookup', # noqa - 'expressions.tests.BasicExpressionsTests.test_ticket_18375_chained_filters', # noqa - 'expressions.tests.BasicExpressionsTests.test_ticket_18375_join_reuse', # noqa - 'expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering', # noqa - 'expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering_2', # noqa - 'expressions.tests.BasicExpressionsTests.test_update', # noqa - 'expressions.tests.BasicExpressionsTests.test_update_inherited_field_value', # noqa - 'expressions.tests.BasicExpressionsTests.test_update_with_fk', # noqa - 'expressions.tests.BasicExpressionsTests.test_update_with_none', # noqa - 'expressions.tests.BasicExpressionsTests.test_uuid_pk_subquery', # noqa - 'expressions.tests.ExpressionsNumericTests.test_complex_expressions', # noqa - 'expressions.tests.ExpressionsNumericTests.test_fill_with_value_from_same_object', # noqa - 'expressions.tests.ExpressionsNumericTests.test_filter_not_equals_other_field', # noqa - 'expressions.tests.ExpressionsNumericTests.test_increment_value', # noqa - 'expressions.tests.ExpressionsTests.test_F_reuse', # noqa - 'expressions.tests.IterableLookupInnerExpressionsTests.test_expressions_in_lookups_join_choice', # noqa - 'expressions.tests.IterableLookupInnerExpressionsTests.test_in_lookup_allows_F_expressions_and_expressions_for_datetimes', # noqa - 'expressions.tests.IterableLookupInnerExpressionsTests.test_in_lookup_allows_F_expressions_and_expressions_for_integers', # noqa - 'expressions.tests.IterableLookupInnerExpressionsTests.test_range_lookup_allows_F_expressions_and_expressions_for_integers', # noqa - 'expressions.tests.ValueTests.test_update_TimeField_using_Value', # noqa - 'expressions.tests.ValueTests.test_update_UUIDField_using_Value', # noqa - 'fixtures.tests.FixtureLoadingTests.test_loaddata_error_message', # noqa - 'fixtures.tests.ForwardReferenceTests.test_forward_reference_fk', # noqa - 'fixtures.tests.ForwardReferenceTests.test_forward_reference_m2m', # noqa - 'get_or_create.tests.GetOrCreateTests.test_get_or_create_invalid_params', # noqa - 'get_or_create.tests.GetOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key', # noqa - 'get_or_create.tests.GetOrCreateTestsWithManualPKs.test_get_or_create_raises_IntegrityError_plus_traceback', # noqa - 'introspection.tests.IntrospectionTests.test_get_constraints', # noqa - 'introspection.tests.IntrospectionTests.test_get_constraints_index_types', # noqa - 'introspection.tests.IntrospectionTests.test_get_constraints_indexes_orders', # noqa - 'introspection.tests.IntrospectionTests.test_get_primary_key_column', # noqa - 'lookup.tests.LookupTests.test_custom_field_none_rhs', # noqa - 'lookup.tests.LookupTests.test_custom_lookup_none_rhs', # noqa - 'lookup.tests.LookupTests.test_escaping', # noqa - 'lookup.tests.LookupTests.test_exact_none_transform', # noqa - 'lookup.tests.LookupTests.test_exclude', # noqa - 'lookup.tests.LookupTests.test_in_bulk_lots_of_ids', # noqa - 'lookup.tests.LookupTests.test_lookup_collision', # noqa - 'lookup.tests.LookupTests.test_regex', # noqa - 'lookup.tests.LookupTests.test_regex_non_string', # noqa - 'lookup.tests.LookupTests.test_regex_null', # noqa - 'm2m_through.tests.M2mThroughReferentialTests.test_through_fields_self_referential', # noqa - 'm2m_through.tests.M2mThroughTests.test_add_on_m2m_with_intermediate_model_value_required_fails', # noqa - 'm2m_through.tests.M2mThroughTests.test_add_on_reverse_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_clear_on_reverse_removes_all_the_m2m_relationships', # noqa - 'm2m_through.tests.M2mThroughTests.test_clear_removes_all_the_m2m_relationships', # noqa - 'm2m_through.tests.M2mThroughTests.test_create_on_m2m_with_intermediate_model_value_required_fails', # noqa - 'm2m_through.tests.M2mThroughTests.test_create_on_reverse_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_custom_related_name_doesnt_conflict_with_fky_related_name', # noqa - 'm2m_through.tests.M2mThroughTests.test_custom_related_name_forward_non_empty_qs', # noqa - 'm2m_through.tests.M2mThroughTests.test_custom_related_name_reverse_non_empty_qs', # noqa - 'm2m_through.tests.M2mThroughTests.test_filter_on_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_get_on_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_get_or_create_on_m2m_with_intermediate_model_value_required_fails', # noqa - 'm2m_through.tests.M2mThroughTests.test_order_by_relational_field_through_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_first_model_by_intermediate_model_attribute', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_model_by_attribute_name_of_related_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_model_by_custom_related_name', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_model_by_intermediate_can_return_non_unique_queryset', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_model_by_related_model_name', # noqa - 'm2m_through.tests.M2mThroughTests.test_query_second_model_by_intermediate_model_attribute', # noqa - 'm2m_through.tests.M2mThroughTests.test_remove_on_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_remove_on_reverse_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_retrieve_intermediate_items', # noqa - 'm2m_through.tests.M2mThroughTests.test_retrieve_reverse_intermediate_items', # noqa - 'm2m_through.tests.M2mThroughTests.test_set_on_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_set_on_m2m_with_intermediate_model_value_required_fails', # noqa - 'm2m_through.tests.M2mThroughTests.test_set_on_reverse_m2m_with_intermediate_model', # noqa - 'm2m_through.tests.M2mThroughTests.test_update_or_create_on_m2m_with_intermediate_model_value_required_fails', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_join_trimming_forwards', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_join_trimming_reverse', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_retrieve_forward_m2m_items', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_retrieve_forward_m2m_items_via_custom_id_intermediary', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_retrieve_reverse_m2m_items', # noqa - 'm2m_through_regress.tests.M2MThroughTestCase.test_retrieve_reverse_m2m_items_via_custom_id_intermediary', # noqa - 'm2m_through_regress.tests.ThroughLoadDataTestCase.test_sequence_creation', # noqa - 'm2m_through_regress.tests.ToFieldThroughTests.test_add_null_reverse', # noqa - 'm2m_through_regress.tests.ToFieldThroughTests.test_add_null_reverse_related', # noqa - 'm2m_through_regress.tests.ToFieldThroughTests.test_add_related_null', # noqa - 'm2o_recursive.tests.ManyToOneRecursiveTests.test_m2o_recursive', # noqa - 'm2o_recursive.tests.MultipleManyToOneRecursiveTests.test_m2o_recursive2', # noqa - 'managers_regress.tests.ManagersRegressionTests.test_field_can_be_called_exact', # noqa - 'managers_regress.tests.ManagersRegressionTests.test_regress_3871', # noqa - 'many_to_one.tests.ManyToOneTests.test_add_after_prefetch', # noqa - 'many_to_one.tests.ManyToOneTests.test_add_then_remove_after_prefetch', # noqa - 'many_to_one.tests.ManyToOneTests.test_cached_foreign_key_with_to_field_not_cleared_by_save', # noqa - 'many_to_one.tests.ManyToOneTests.test_multiple_foreignkeys', # noqa - 'many_to_one.tests.ManyToOneTests.test_reverse_foreign_key_instance_to_field_caching', # noqa - 'many_to_one.tests.ManyToOneTests.test_set_after_prefetch', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_add_efficiency', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_assign_clear_related_set', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_assign_with_queryset', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_clear_efficiency', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_created_via_related_set', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_created_without_related', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_get_related', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_related_null_to_field', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_related_set', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_remove_from_wrong_set', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_set', # noqa - 'many_to_one_null.tests.ManyToOneNullTests.test_set_clear_non_bulk', # noqa - 'migrations.test_operations.OperationTests.test_add_binaryfield', # noqa - 'migrations.test_operations.OperationTests.test_add_charfield', # noqa - 'migrations.test_operations.OperationTests.test_add_constraint', # noqa - 'migrations.test_operations.OperationTests.test_add_constraint_percent_escaping', # noqa - 'migrations.test_operations.OperationTests.test_add_field', # noqa - 'migrations.test_operations.OperationTests.test_add_field_m2m', # noqa - 'migrations.test_operations.OperationTests.test_add_field_preserve_default', # noqa - 'migrations.test_operations.OperationTests.test_add_index', # noqa - 'migrations.test_operations.OperationTests.test_add_index_state_forwards', # noqa - 'migrations.test_operations.OperationTests.test_add_or_constraint', # noqa - 'migrations.test_operations.OperationTests.test_add_partial_unique_constraint', # noqa - 'migrations.test_operations.OperationTests.test_add_textfield', # noqa - 'migrations.test_operations.OperationTests.test_alter_field', # noqa - 'migrations.test_operations.OperationTests.test_alter_field_m2m', # noqa - 'migrations.test_operations.OperationTests.test_alter_field_pk', # noqa - 'migrations.test_operations.OperationTests.test_alter_field_pk_fk', # noqa - 'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_target_changes', # noqa - 'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_with_to_field_target_changes', # noqa - 'migrations.test_operations.OperationTests.test_alter_fk', # noqa - 'migrations.test_operations.OperationTests.test_alter_index_together', # noqa - 'migrations.test_operations.OperationTests.test_alter_index_together_remove', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_managers', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_managers_emptying', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_options', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_options_emptying', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_table_none', # noqa - 'migrations.test_operations.OperationTests.test_alter_model_table_noop', # noqa - 'migrations.test_operations.OperationTests.test_alter_unique_together', # noqa - 'migrations.test_operations.OperationTests.test_alter_unique_together_remove', # noqa - 'migrations.test_operations.OperationTests.test_autofield_foreignfield_growth', # noqa - 'migrations.test_operations.OperationTests.test_column_name_quoting', # noqa - 'migrations.test_operations.OperationTests.test_create_model', # noqa - 'migrations.test_operations.OperationTests.test_create_model_inheritance', # noqa - 'migrations.test_operations.OperationTests.test_create_model_m2m', # noqa - 'migrations.test_operations.OperationTests.test_create_model_managers', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_constraint', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_duplicate_base', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_duplicate_field_name', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_duplicate_manager_name', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_partial_unique_constraint', # noqa - 'migrations.test_operations.OperationTests.test_create_model_with_unique_after', # noqa - 'migrations.test_operations.OperationTests.test_create_proxy_model', # noqa - 'migrations.test_operations.OperationTests.test_create_unmanaged_model', # noqa - 'migrations.test_operations.OperationTests.test_delete_model', # noqa - 'migrations.test_operations.OperationTests.test_delete_mti_model', # noqa - 'migrations.test_operations.OperationTests.test_delete_proxy_model', # noqa - 'migrations.test_operations.OperationTests.test_model_with_bigautofield', # noqa - 'migrations.test_operations.OperationTests.test_remove_constraint', # noqa - 'migrations.test_operations.OperationTests.test_remove_field', # noqa - 'migrations.test_operations.OperationTests.test_remove_field_m2m', # noqa - 'migrations.test_operations.OperationTests.test_remove_field_m2m_with_through', # noqa - 'migrations.test_operations.OperationTests.test_remove_fk', # noqa - 'migrations.test_operations.OperationTests.test_remove_index', # noqa - 'migrations.test_operations.OperationTests.test_remove_index_state_forwards', # noqa - 'migrations.test_operations.OperationTests.test_remove_partial_unique_constraint', # noqa - 'migrations.test_operations.OperationTests.test_rename_missing_field', # noqa - 'migrations.test_operations.OperationTests.test_rename_model_state_forwards', # noqa - 'migrations.test_operations.OperationTests.test_rename_referenced_field_state_forward', # noqa - 'migrations.test_operations.OperationTests.test_run_python', # noqa - 'migrations.test_operations.OperationTests.test_run_python_atomic', # noqa - 'migrations.test_operations.OperationTests.test_run_python_noop', # noqa - 'migrations.test_operations.OperationTests.test_run_python_related_assignment', # noqa - 'migrations.test_operations.OperationTests.test_run_sql', # noqa - 'migrations.test_operations.OperationTests.test_run_sql_noop', # noqa - 'migrations.test_operations.OperationTests.test_run_sql_params_invalid', # noqa - 'migrations.test_operations.OperationTests.test_separate_database_and_state', # noqa - 'migrations.test_operations.OperationTests.test_separate_database_and_state2', # noqa - 'model_fields.test_booleanfield.BooleanFieldTests.test_null_default', # noqa - 'model_fields.test_durationfield.TestSaveLoad.test_create_empty', # noqa - 'model_fields.test_genericipaddressfield.GenericIPAddressFieldTests.test_blank_string_saved_as_null', # noqa - 'model_fields.test_genericipaddressfield.GenericIPAddressFieldTests.test_null_value', # noqa - 'model_fields.test_imagefield.TwoImageFieldTests.test_dimensions', # noqa - 'model_fields.test_imagefield.TwoImageFieldTests.test_field_save_and_delete_methods', # noqa - 'model_fields.test_integerfield.BigIntegerFieldTests.test_backend_range_save', # noqa - 'model_fields.test_integerfield.BigIntegerFieldTests.test_coercing', # noqa - 'model_fields.test_integerfield.BigIntegerFieldTests.test_documented_range', # noqa - 'model_fields.test_integerfield.BigIntegerFieldTests.test_types', # noqa - 'model_fields.test_uuid.TestQuerying.test_exact', # noqa - 'model_fields.test_uuid.TestQuerying.test_isnull', # noqa - 'model_fields.test_uuid.TestSaveLoad.test_null_handling', # noqa - 'multiple_database.tests.FixtureTestCase.test_fixture_loading', # noqa - 'multiple_database.tests.FixtureTestCase.test_pseudo_empty_fixtures', # noqa - 'multiple_database.tests.PickleQuerySetTestCase.test_pickling', # noqa - 'multiple_database.tests.QueryTestCase.test_basic_queries', # noqa - 'multiple_database.tests.QueryTestCase.test_default_creation', # noqa - 'multiple_database.tests.QueryTestCase.test_foreign_key_cross_database_protection', # noqa - 'multiple_database.tests.QueryTestCase.test_foreign_key_reverse_operations', # noqa - 'multiple_database.tests.QueryTestCase.test_foreign_key_separation', # noqa - 'multiple_database.tests.QueryTestCase.test_generic_key_cross_database_protection', # noqa - 'multiple_database.tests.QueryTestCase.test_generic_key_deletion', # noqa - 'multiple_database.tests.QueryTestCase.test_generic_key_reverse_operations', # noqa - 'multiple_database.tests.QueryTestCase.test_generic_key_separation', # noqa - 'multiple_database.tests.QueryTestCase.test_m2m_cross_database_protection', # noqa - 'multiple_database.tests.QueryTestCase.test_m2m_deletion', # noqa - 'multiple_database.tests.QueryTestCase.test_m2m_forward_operations', # noqa - 'multiple_database.tests.QueryTestCase.test_m2m_reverse_operations', # noqa - 'multiple_database.tests.QueryTestCase.test_m2m_separation', # noqa - 'multiple_database.tests.QueryTestCase.test_o2o_cross_database_protection', # noqa - 'multiple_database.tests.QueryTestCase.test_o2o_separation', # noqa - 'multiple_database.tests.QueryTestCase.test_ordering', # noqa - 'multiple_database.tests.QueryTestCase.test_other_creation', # noqa - 'multiple_database.tests.QueryTestCase.test_raw', # noqa - 'multiple_database.tests.QueryTestCase.test_refresh', # noqa - 'multiple_database.tests.QueryTestCase.test_refresh_router_instance_hint', # noqa - 'multiple_database.tests.QueryTestCase.test_related_manager', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_add', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_clear', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_delete', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_get_or_create', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_remove', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_m2m_update', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_add', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_clear', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_delete', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_get_or_create', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_remove', # noqa - 'multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_update', # noqa - 'multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_delete', # noqa - 'multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_m2m', # noqa - 'multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_read', # noqa - 'multiple_database.tests.RouterModelArgumentTestCase.test_m2m_collection', # noqa - 'multiple_database.tests.RouterTestCase.test_database_routing', # noqa - 'multiple_database.tests.RouterTestCase.test_foreign_key_cross_database_protection', # noqa - 'multiple_database.tests.RouterTestCase.test_generic_key_managers', # noqa - 'multiple_database.tests.RouterTestCase.test_invalid_set_foreign_key_assignment', # noqa - 'multiple_database.tests.RouterTestCase.test_m2m_cross_database_protection', # noqa - 'multiple_database.tests.RouterTestCase.test_m2m_managers', # noqa - 'multiple_database.tests.RouterTestCase.test_o2o_cross_database_protection', # noqa - 'multiple_database.tests.RouterTestCase.test_partial_router', # noqa - 'multiple_database.tests.SignalTests.test_database_arg_m2m', # noqa - 'null_fk.tests.NullFkTests.test_combine_isnull', # noqa - 'null_fk.tests.NullFkTests.test_null_fk', # noqa - 'null_fk_ordering.tests.NullFkOrderingTests.test_ordering_across_null_fk', # noqa - 'null_queries.tests.NullQueriesTests.test_reverse_relations', # noqa - 'ordering.tests.OrderingTests.test_default_ordering', # noqa - 'ordering.tests.OrderingTests.test_default_ordering_override', # noqa - 'ordering.tests.OrderingTests.test_deprecated_values_annotate', # noqa - 'ordering.tests.OrderingTests.test_extra_ordering', # noqa - 'ordering.tests.OrderingTests.test_extra_ordering_quoting', # noqa - 'ordering.tests.OrderingTests.test_extra_ordering_with_table_name', # noqa - 'ordering.tests.OrderingTests.test_no_reordering_after_slicing', # noqa - 'ordering.tests.OrderingTests.test_order_by_f_expression', # noqa - 'ordering.tests.OrderingTests.test_order_by_f_expression_duplicates', # noqa - 'ordering.tests.OrderingTests.test_order_by_nulls_first', # noqa - 'ordering.tests.OrderingTests.test_order_by_nulls_first_and_last', # noqa - 'ordering.tests.OrderingTests.test_order_by_nulls_last', # noqa - 'ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery', # noqa - 'ordering.tests.OrderingTests.test_related_ordering_duplicate_table_reference', # noqa - 'ordering.tests.OrderingTests.test_reverse_ordering_pure', # noqa - 'ordering.tests.OrderingTests.test_reversed_ordering', # noqa - 'ordering.tests.OrderingTests.test_stop_slicing', # noqa - 'ordering.tests.OrderingTests.test_stop_start_slicing', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_batch_size', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_foreign_keys_do_not_lookup', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_functions', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_set_field_to_null', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_set_mixed_fields_to_null', # noqa - 'queries.test_bulk_update.BulkUpdateNoteTests.test_simple', # noqa - 'queries.test_bulk_update.BulkUpdateTests.test_custom_db_columns', # noqa - 'queries.test_bulk_update.BulkUpdateTests.test_field_references', # noqa - 'queries.test_bulk_update.BulkUpdateTests.test_ipaddressfield', # noqa - 'queries.tests.CloneTests.test_evaluated_queryset_as_argument', # noqa - 'queries.tests.ComparisonTests.test_ticket8597', # noqa - 'queries.tests.ConditionalTests.test_in_list_limit', # noqa - 'queries.tests.ConditionalTests.test_infinite_loop', # noqa - 'queries.tests.ConditionalTests.test_null_ordering_added', # noqa - 'queries.tests.DisjunctionPromotionTests.test_disjunction_promotion_select_related', # noqa - 'queries.tests.DisjunctiveFilterTests.test_ticket7872', # noqa - 'queries.tests.DisjunctiveFilterTests.test_ticket8283', # noqa - 'queries.tests.IsNullTests.test_primary_key', # noqa - 'queries.tests.IsNullTests.test_to_field', # noqa - 'queries.tests.JoinReuseTest.test_inverted_q_across_relations', # noqa - 'queries.tests.NullInExcludeTest.test_col_not_in_list_containing_null', # noqa - 'queries.tests.NullInExcludeTest.test_double_exclude', # noqa - 'queries.tests.NullInExcludeTest.test_null_in_exclude_qs', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_isnull_filter_promotion', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_null_join_demotion', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_17886', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_21366', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_21748', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_21748_complex_filter', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_21748_double_negated_and', # noqa - 'queries.tests.NullJoinPromotionOrTest.test_ticket_21748_double_negated_or', # noqa - 'queries.tests.NullableRelOrderingTests.test_join_already_in_query', # noqa - 'queries.tests.NullableRelOrderingTests.test_ticket10028', # noqa - 'queries.tests.Queries1Tests.test_avoid_infinite_loop_on_too_many_subqueries', # noqa - 'queries.tests.Queries1Tests.test_common_mixed_case_foreign_keys', # noqa - 'queries.tests.Queries1Tests.test_deferred_load_qs_pickling', # noqa - 'queries.tests.Queries1Tests.test_double_exclude', # noqa - 'queries.tests.Queries1Tests.test_error_raised_on_filter_with_dictionary', # noqa - 'queries.tests.Queries1Tests.test_exclude', # noqa - 'queries.tests.Queries1Tests.test_exclude_in', # noqa - 'queries.tests.Queries1Tests.test_get_clears_ordering', # noqa - 'queries.tests.Queries1Tests.test_heterogeneous_qs_combination', # noqa - 'queries.tests.Queries1Tests.test_lookup_constraint_fielderror', # noqa - 'queries.tests.Queries1Tests.test_nested_exclude', # noqa - 'queries.tests.Queries1Tests.test_order_by_join_unref', # noqa - 'queries.tests.Queries1Tests.test_order_by_tables', # noqa - 'queries.tests.Queries1Tests.test_reasonable_number_of_subq_aliases', # noqa - 'queries.tests.Queries1Tests.test_subquery_condition', # noqa - 'queries.tests.Queries1Tests.test_ticket10205', # noqa - 'queries.tests.Queries1Tests.test_ticket10432', # noqa - 'queries.tests.Queries1Tests.test_ticket1050', # noqa - 'queries.tests.Queries1Tests.test_ticket10742', # noqa - 'queries.tests.Queries1Tests.test_ticket17429', # noqa - 'queries.tests.Queries1Tests.test_ticket1801', # noqa - 'queries.tests.Queries1Tests.test_ticket19672', # noqa - 'queries.tests.Queries1Tests.test_ticket2091', # noqa - 'queries.tests.Queries1Tests.test_ticket2253', # noqa - 'queries.tests.Queries1Tests.test_ticket2306', # noqa - 'queries.tests.Queries1Tests.test_ticket2400', # noqa - 'queries.tests.Queries1Tests.test_ticket2496', # noqa - 'queries.tests.Queries1Tests.test_ticket2902', # noqa - 'queries.tests.Queries1Tests.test_ticket3037', # noqa - 'queries.tests.Queries1Tests.test_ticket3141', # noqa - 'queries.tests.Queries1Tests.test_ticket4358', # noqa - 'queries.tests.Queries1Tests.test_ticket4464', # noqa - 'queries.tests.Queries1Tests.test_ticket4510', # noqa - 'queries.tests.Queries1Tests.test_ticket6074', # noqa - 'queries.tests.Queries1Tests.test_ticket6154', # noqa - 'queries.tests.Queries1Tests.test_ticket6981', # noqa - 'queries.tests.Queries1Tests.test_ticket7076', # noqa - 'queries.tests.Queries1Tests.test_ticket7096', # noqa - 'queries.tests.Queries1Tests.test_ticket7098', # noqa - 'queries.tests.Queries1Tests.test_ticket7155', # noqa - 'queries.tests.Queries1Tests.test_ticket7181', # noqa - 'queries.tests.Queries1Tests.test_ticket7235', # noqa - 'queries.tests.Queries1Tests.test_ticket7277', # noqa - 'queries.tests.Queries1Tests.test_ticket7323', # noqa - 'queries.tests.Queries1Tests.test_ticket7378', # noqa - 'queries.tests.Queries1Tests.test_ticket7791', # noqa - 'queries.tests.Queries1Tests.test_ticket7813', # noqa - 'queries.tests.Queries1Tests.test_ticket8439', # noqa - 'queries.tests.Queries1Tests.test_ticket9926', # noqa - 'queries.tests.Queries1Tests.test_ticket9985', # noqa - 'queries.tests.Queries1Tests.test_ticket9997', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_1', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_2', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_3', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_4', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_5', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_6', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_7', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_8', # noqa - 'queries.tests.Queries1Tests.test_ticket_10790_combine', # noqa - 'queries.tests.Queries1Tests.test_ticket_20250', # noqa - 'queries.tests.Queries1Tests.test_tickets_1878_2939', # noqa - 'queries.tests.Queries1Tests.test_tickets_2076_7256', # noqa - 'queries.tests.Queries1Tests.test_tickets_2080_3592', # noqa - 'queries.tests.Queries1Tests.test_tickets_2874_3002', # noqa - 'queries.tests.Queries1Tests.test_tickets_4088_4306', # noqa - 'queries.tests.Queries1Tests.test_tickets_5321_7070', # noqa - 'queries.tests.Queries1Tests.test_tickets_5324_6704', # noqa - 'queries.tests.Queries1Tests.test_tickets_6180_6203', # noqa - 'queries.tests.Queries1Tests.test_tickets_7087_12242', # noqa - 'queries.tests.Queries1Tests.test_tickets_7204_7506', # noqa - 'queries.tests.Queries1Tests.test_tickets_7448_7707', # noqa - 'queries.tests.Queries2Tests.test_ticket12239', # noqa - 'queries.tests.Queries2Tests.test_ticket4289', # noqa - 'queries.tests.Queries2Tests.test_ticket7759', # noqa - 'queries.tests.Queries4Tests.test_combine_join_reuse', # noqa - 'queries.tests.Queries4Tests.test_join_reuse_order', # noqa - 'queries.tests.Queries4Tests.test_order_by_resetting', # noqa - 'queries.tests.Queries4Tests.test_order_by_reverse_fk', # noqa - 'queries.tests.Queries4Tests.test_ticket10181', # noqa - 'queries.tests.Queries4Tests.test_ticket11811', # noqa - 'queries.tests.Queries4Tests.test_ticket14876', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_exclude_false', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_filter_false', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_filter_true', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_one2one_exclude_false', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_one2one_exclude_true', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_one2one_filter_false', # noqa - 'queries.tests.Queries4Tests.test_ticket15316_one2one_filter_true', # noqa - 'queries.tests.Queries4Tests.test_ticket24525', # noqa - 'queries.tests.Queries4Tests.test_ticket7095', # noqa - 'queries.tests.Queries5Tests.test_extra_select_literal_percent_s', # noqa - 'queries.tests.Queries5Tests.test_ordering', # noqa - 'queries.tests.Queries5Tests.test_ticket5261', # noqa - 'queries.tests.Queries5Tests.test_ticket7045', # noqa - 'queries.tests.Queries5Tests.test_ticket9848', # noqa - 'queries.tests.Queries6Tests.test_distinct_ordered_sliced_subquery_aggregation', # noqa - 'queries.tests.Queries6Tests.test_multiple_columns_with_the_same_name_slice', # noqa - 'queries.tests.Queries6Tests.test_nested_queries_sql', # noqa - 'queries.tests.Queries6Tests.test_parallel_iterators', # noqa - 'queries.tests.Queries6Tests.test_ticket3739', # noqa - 'queries.tests.Queries6Tests.test_ticket_11320', # noqa - 'queries.tests.Queries6Tests.test_tickets_8921_9188', # noqa - 'queries.tests.RawQueriesTests.test_ticket14729', # noqa - 'queries.tests.RelabelCloneTest.test_ticket_19964', # noqa - 'queries.tests.RelatedLookupTypeTests.test_correct_lookup', # noqa - 'queries.tests.RelatedLookupTypeTests.test_wrong_backward_lookup', # noqa - 'queries.tests.RelatedLookupTypeTests.test_wrong_type_lookup', # noqa - 'queries.tests.ReverseJoinTrimmingTest.test_reverse_trimming', # noqa - 'queries.tests.SubclassFKTests.test_ticket7778', # noqa - 'queries.tests.Ticket20101Tests.test_ticket_20101', # noqa - 'queries.tests.Ticket22429Tests.test_ticket_22429', # noqa - 'queries.tests.ToFieldTests.test_nested_in_subquery', # noqa - 'queries.tests.ToFieldTests.test_recursive_fk', # noqa - 'queries.tests.ToFieldTests.test_recursive_fk_reverse', # noqa - 'queries.tests.ValuesJoinPromotionTests.test_ticket_21376', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_multiple_select_params_values_order_by', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_select_params_values_order_in_extra', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_values', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_values_list', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_values_order_in_extra', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_values_order_multiple', # noqa - 'queries.tests.ValuesQuerysetTests.test_extra_values_order_twice', # noqa - 'queries.tests.ValuesQuerysetTests.test_field_error_values_list', # noqa - 'queries.tests.ValuesQuerysetTests.test_flat_extra_values_list', # noqa - 'queries.tests.ValuesQuerysetTests.test_flat_values_list', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_bad_field_name', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_expression', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_expression_with_default_alias', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_flat', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_with_fields', # noqa - 'queries.tests.ValuesQuerysetTests.test_named_values_list_without_fields', # noqa - 'queries.tests.WeirdQuerysetSlicingTests.test_empty_resultset_sql', # noqa - 'queries.tests.WeirdQuerysetSlicingTests.test_empty_sliced_subquery', # noqa - 'queries.tests.WeirdQuerysetSlicingTests.test_empty_sliced_subquery_exclude', # noqa - 'queries.tests.WeirdQuerysetSlicingTests.test_tickets_7698_10202', # noqa - 'queries.tests.WeirdQuerysetSlicingTests.test_zero_length_values_slicing', # noqa - 'schema.tests.SchemaTests.test_add_datefield_and_datetimefield_use_effective_default', # noqa - 'schema.tests.SchemaTests.test_add_field', # noqa - 'schema.tests.SchemaTests.test_add_field_binary', # noqa - 'schema.tests.SchemaTests.test_add_field_default_dropped', # noqa - 'schema.tests.SchemaTests.test_add_field_default_transform', # noqa - 'schema.tests.SchemaTests.test_add_field_remove_field', # noqa - 'schema.tests.SchemaTests.test_add_field_temp_default', # noqa - 'schema.tests.SchemaTests.test_add_field_temp_default_boolean', # noqa - 'schema.tests.SchemaTests.test_add_field_use_effective_default', # noqa - 'schema.tests.SchemaTests.test_add_foreign_key_long_names', # noqa - 'schema.tests.SchemaTests.test_add_foreign_key_quoted_db_table', # noqa - 'schema.tests.SchemaTests.test_add_foreign_object', # noqa - 'schema.tests.SchemaTests.test_add_remove_index', # noqa - 'schema.tests.SchemaTests.test_add_textfield_unhashable_default', # noqa - 'schema.tests.SchemaTests.test_alter', # noqa - 'schema.tests.SchemaTests.test_alter_auto_field_to_integer_field', # noqa - 'schema.tests.SchemaTests.test_alter_charfield_to_null', # noqa - 'schema.tests.SchemaTests.test_alter_field_add_index_to_integerfield', # noqa - 'schema.tests.SchemaTests.test_alter_field_default_doesnt_perfom_queries', # noqa - 'schema.tests.SchemaTests.test_alter_field_default_dropped', # noqa - 'schema.tests.SchemaTests.test_alter_field_fk_keeps_index', # noqa - 'schema.tests.SchemaTests.test_alter_field_fk_to_o2o', # noqa - 'schema.tests.SchemaTests.test_alter_field_o2o_keeps_unique', # noqa - 'schema.tests.SchemaTests.test_alter_field_o2o_to_fk', # noqa - 'schema.tests.SchemaTests.test_alter_fk', # noqa - 'schema.tests.SchemaTests.test_alter_fk_checks_deferred_constraints', # noqa - 'schema.tests.SchemaTests.test_alter_fk_to_o2o', # noqa - 'schema.tests.SchemaTests.test_alter_implicit_id_to_explicit', # noqa - 'schema.tests.SchemaTests.test_alter_int_pk_to_autofield_pk', # noqa - 'schema.tests.SchemaTests.test_alter_int_pk_to_bigautofield_pk', # noqa - 'schema.tests.SchemaTests.test_alter_null_to_not_null', # noqa - 'schema.tests.SchemaTests.test_alter_null_to_not_null_keeping_default', # noqa - 'schema.tests.SchemaTests.test_alter_numeric_field_keep_null_status', # noqa - 'schema.tests.SchemaTests.test_alter_o2o_to_fk', # noqa - 'schema.tests.SchemaTests.test_alter_text_field', # noqa - 'schema.tests.SchemaTests.test_alter_textfield_to_null', # noqa - 'schema.tests.SchemaTests.test_alter_textual_field_keep_null_status', # noqa - 'schema.tests.SchemaTests.test_alter_to_fk', # noqa - 'schema.tests.SchemaTests.test_char_field_with_db_index_to_fk', # noqa - 'schema.tests.SchemaTests.test_check_constraints', # noqa - 'schema.tests.SchemaTests.test_context_manager_exit', # noqa - 'schema.tests.SchemaTests.test_create_index_together', # noqa - 'schema.tests.SchemaTests.test_creation_deletion', # noqa - 'schema.tests.SchemaTests.test_creation_deletion_reserved_names', # noqa - 'schema.tests.SchemaTests.test_fk', # noqa - 'schema.tests.SchemaTests.test_fk_db_constraint', # noqa - 'schema.tests.SchemaTests.test_fk_to_proxy', # noqa - 'schema.tests.SchemaTests.test_foreign_key_index_long_names_regression', # noqa - 'schema.tests.SchemaTests.test_index_together', # noqa - 'schema.tests.SchemaTests.test_index_together_with_fk', # noqa - 'schema.tests.SchemaTests.test_indexes', # noqa - 'schema.tests.SchemaTests.test_m2m', # noqa - 'schema.tests.SchemaTests.test_m2m_create', # noqa - 'schema.tests.SchemaTests.test_m2m_create_custom', # noqa - 'schema.tests.SchemaTests.test_m2m_create_inherited', # noqa - 'schema.tests.SchemaTests.test_m2m_create_through', # noqa - 'schema.tests.SchemaTests.test_m2m_create_through_custom', # noqa - 'schema.tests.SchemaTests.test_m2m_create_through_inherited', # noqa - 'schema.tests.SchemaTests.test_m2m_custom', # noqa - 'schema.tests.SchemaTests.test_m2m_db_constraint', # noqa - 'schema.tests.SchemaTests.test_m2m_db_constraint_custom', # noqa - 'schema.tests.SchemaTests.test_m2m_db_constraint_inherited', # noqa - 'schema.tests.SchemaTests.test_m2m_inherited', # noqa - 'schema.tests.SchemaTests.test_m2m_through_alter', # noqa - 'schema.tests.SchemaTests.test_m2m_through_alter_custom', # noqa - 'schema.tests.SchemaTests.test_m2m_through_alter_inherited', # noqa - 'schema.tests.SchemaTests.test_namespaced_db_table_create_index_name', # noqa - 'schema.tests.SchemaTests.test_no_db_constraint_added_during_primary_key_change', # noqa - 'schema.tests.SchemaTests.test_order_index', # noqa - 'schema.tests.SchemaTests.test_remove_constraints_capital_letters', # noqa - 'schema.tests.SchemaTests.test_remove_db_index_doesnt_remove_custom_indexes', # noqa - 'schema.tests.SchemaTests.test_remove_field_check_does_not_remove_meta_constraints', # noqa - 'schema.tests.SchemaTests.test_remove_field_unique_does_not_remove_meta_constraints', # noqa - 'schema.tests.SchemaTests.test_remove_index_together_does_not_remove_meta_indexes', # noqa - 'schema.tests.SchemaTests.test_remove_unique_together_does_not_remove_meta_constraints', # noqa - 'schema.tests.SchemaTests.test_text_field_with_db_index', # noqa - 'schema.tests.SchemaTests.test_text_field_with_db_index_to_fk', # noqa - 'schema.tests.SchemaTests.test_unique', # noqa - 'schema.tests.SchemaTests.test_unique_and_reverse_m2m', # noqa - 'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops', # noqa - 'schema.tests.SchemaTests.test_unique_together', # noqa - 'schema.tests.SchemaTests.test_unique_together_with_fk', # noqa - 'schema.tests.SchemaTests.test_unique_together_with_fk_with_existing_index', # noqa - 'schema.tests.SchemaTests.test_unsupported_transactional_ddl_disallowed', # noqa - 'select_related_onetoone.tests.ReverseSelectRelatedTestCase.test_nullable_relation', # noqa - 'select_related_onetoone.tests.ReverseSelectRelatedTestCase.test_self_relation', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_actual_expiry', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_clearsessions_command', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_cycle', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_cycle_with_no_session_cache', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_delete', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_flush', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_invalid_key', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_save', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_save_doesnt_clear_data', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_session_get_decoded', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_session_save_does_not_resurrect_session_logged_out_in_other_context', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_session_str', # noqa - 'sessions_tests.tests.CustomDatabaseSessionTests.test_sessionmanager_save', # noqa - 'sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap', # noqa - 'sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap_attributes', # noqa - 'sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap_lastmod', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_cached_sitemap_index', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_empty_page', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_empty_sitemap', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_localized_priority', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_no_section', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_page_not_int', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_paged_sitemap', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_requestsite_sitemap', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_custom_sitemap', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_i18nsitemap_index', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_custom_index', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_index', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_section', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_get_urls_no_site_1', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_get_urls_no_site_2', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_item', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_date', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_missing', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_mixed', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_tz', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_not_callable', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_without_entries', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_ascending', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_descending', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_mixed_ascending_last_modified_missing', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_mixed_descending_last_modified_missing', # noqa - 'sitemaps_tests.test_http.HTTPSitemapTests.test_x_robots_sitemap', # noqa - 'sitemaps_tests.test_https.HTTPSDetectionSitemapTests.test_sitemap_index_with_https_request', # noqa - 'sitemaps_tests.test_https.HTTPSDetectionSitemapTests.test_sitemap_section_with_https_request', # noqa - 'sitemaps_tests.test_https.HTTPSSitemapTests.test_secure_sitemap_index', # noqa - 'sitemaps_tests.test_https.HTTPSSitemapTests.test_secure_sitemap_section', # noqa - 'sitemaps_tests.test_management.PingGoogleTests.test_args', # noqa - 'sitemaps_tests.test_management.PingGoogleTests.test_default', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_exact_url', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_global', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_index', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_insecure', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_no_sites', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_not_detected', # noqa - 'sitemaps_tests.test_utils.PingGoogleTests.test_something', # noqa - 'string_lookup.tests.StringLookupTests.test_queries_on_textfields', # noqa - 'test_client.tests.ClientTest.test_empty_post', # noqa - 'test_client.tests.ClientTest.test_exception_following_nested_client_request', # noqa - 'test_client.tests.ClientTest.test_external_redirect', # noqa - 'test_client.tests.ClientTest.test_external_redirect_with_fetch_error_msg', # noqa - 'test_client.tests.ClientTest.test_follow_307_and_308_preserves_get_params', # noqa - 'test_client.tests.ClientTest.test_follow_307_and_308_preserves_post_data', # noqa - 'test_client.tests.ClientTest.test_follow_307_and_308_preserves_put_body', # noqa - 'test_client.tests.ClientTest.test_follow_307_and_308_redirect', # noqa - 'test_client.tests.ClientTest.test_follow_redirect', # noqa - 'test_client.tests.ClientTest.test_follow_relative_redirect', # noqa - 'test_client.tests.ClientTest.test_follow_relative_redirect_no_trailing_slash', # noqa - 'test_client.tests.ClientTest.test_force_login_with_backend', # noqa - 'test_client.tests.ClientTest.test_force_login_with_backend_missing_get_user', # noqa - 'test_client.tests.ClientTest.test_force_login_without_backend', # noqa - 'test_client.tests.ClientTest.test_form_error', # noqa - 'test_client.tests.ClientTest.test_form_error_with_template', # noqa - 'test_client.tests.ClientTest.test_get_data_none', # noqa - 'test_client.tests.ClientTest.test_get_post_view', # noqa - 'test_client.tests.ClientTest.test_get_view', # noqa - 'test_client.tests.ClientTest.test_incomplete_data_form', # noqa - 'test_client.tests.ClientTest.test_incomplete_data_form_with_template', # noqa - 'test_client.tests.ClientTest.test_insecure', # noqa - 'test_client.tests.ClientTest.test_json_encoder_argument', # noqa - 'test_client.tests.ClientTest.test_json_serialization', # noqa - 'test_client.tests.ClientTest.test_logout', # noqa - 'test_client.tests.ClientTest.test_logout_cookie_sessions', # noqa - 'test_client.tests.ClientTest.test_logout_with_force_login', # noqa - 'test_client.tests.ClientTest.test_mail_sending', # noqa - 'test_client.tests.ClientTest.test_mass_mail_sending', # noqa - 'test_client.tests.ClientTest.test_notfound_response', # noqa - 'test_client.tests.ClientTest.test_permanent_redirect', # noqa - 'test_client.tests.ClientTest.test_post', # noqa - 'test_client.tests.ClientTest.test_post_data_none', # noqa - 'test_client.tests.ClientTest.test_put', # noqa - 'test_client.tests.ClientTest.test_query_string_encoding', # noqa - 'test_client.tests.ClientTest.test_raw_post', # noqa - 'test_client.tests.ClientTest.test_redirect', # noqa - 'test_client.tests.ClientTest.test_redirect_http', # noqa - 'test_client.tests.ClientTest.test_redirect_https', # noqa - 'test_client.tests.ClientTest.test_redirect_to_strange_location', # noqa - 'test_client.tests.ClientTest.test_redirect_with_query', # noqa - 'test_client.tests.ClientTest.test_redirect_with_query_ordering', # noqa - 'test_client.tests.ClientTest.test_relative_redirect', # noqa - 'test_client.tests.ClientTest.test_relative_redirect_no_trailing_slash', # noqa - 'test_client.tests.ClientTest.test_response_attached_request', # noqa - 'test_client.tests.ClientTest.test_response_headers', # noqa - 'test_client.tests.ClientTest.test_response_raises_multi_arg_exception', # noqa - 'test_client.tests.ClientTest.test_response_resolver_match', # noqa - 'test_client.tests.ClientTest.test_response_resolver_match_redirect_follow', # noqa - 'test_client.tests.ClientTest.test_response_resolver_match_regular_view', # noqa - 'test_client.tests.ClientTest.test_reverse_lazy_decodes', # noqa - 'test_client.tests.ClientTest.test_secure', # noqa - 'test_client.tests.ClientTest.test_session_engine_is_invalid', # noqa - 'test_client.tests.ClientTest.test_session_modifying_view', # noqa - 'test_client.tests.ClientTest.test_sessions_app_is_not_installed', # noqa - 'test_client.tests.ClientTest.test_temporary_redirect', # noqa - 'test_client.tests.ClientTest.test_trace', # noqa - 'test_client.tests.ClientTest.test_unknown_page', # noqa - 'test_client.tests.ClientTest.test_uploading_named_temp_file', # noqa - 'test_client.tests.ClientTest.test_uploading_temp_file', # noqa - 'test_client.tests.ClientTest.test_url_parameters', # noqa - 'test_client.tests.ClientTest.test_valid_form', # noqa - 'test_client.tests.ClientTest.test_valid_form_with_hints', # noqa - 'test_client.tests.ClientTest.test_valid_form_with_template', # noqa - 'test_client.tests.ClientTest.test_view_with_bad_login', # noqa - 'test_client.tests.ClientTest.test_view_with_exception', # noqa - 'test_client.tests.ClientTest.test_view_with_force_login', # noqa - 'test_client.tests.ClientTest.test_view_with_force_login_and_custom_redirect', # noqa - 'test_client.tests.ClientTest.test_view_with_inactive_force_login', # noqa - 'test_client.tests.ClientTest.test_view_with_inactive_login', # noqa - 'test_client.tests.ClientTest.test_view_with_login', # noqa - 'test_client.tests.ClientTest.test_view_with_login_and_custom_redirect', # noqa - 'test_client.tests.ClientTest.test_view_with_login_when_sessions_app_is_not_installed', # noqa - 'test_client.tests.ClientTest.test_view_with_method_force_login', # noqa - 'test_client.tests.ClientTest.test_view_with_method_login', # noqa - 'test_client.tests.ClientTest.test_view_with_method_permissions', # noqa - 'test_client.tests.ClientTest.test_view_with_permissions', # noqa - 'test_client.tests.ClientTest.test_view_with_permissions_exception', # noqa - 'test_client_regress.tests.AssertTemplateUsedTests.test_multiple_context', # noqa - 'test_client_regress.tests.AssertTemplateUsedTests.test_no_context', # noqa - 'test_client_regress.tests.AssertTemplateUsedTests.test_single_context', # noqa - 'test_client_regress.tests.AssertTemplateUsedTests.test_template_rendered_multiple_times', # noqa - 'test_client_regress.tests.ContextTests.test_15368', # noqa - 'test_client_regress.tests.ContextTests.test_contextlist_get', # noqa - 'test_client_regress.tests.ContextTests.test_contextlist_keys', # noqa - 'test_client_regress.tests.ContextTests.test_inherited_context', # noqa - 'test_client_regress.tests.ContextTests.test_nested_requests', # noqa - 'test_client_regress.tests.ContextTests.test_single_context', # noqa - 'test_client_regress.tests.ExceptionTests.test_exception_cleared', # noqa - 'test_client_regress.tests.LoginTests.test_login_different_client', # noqa - 'test_client_regress.tests.SessionEngineTests.test_login', # noqa - 'test_client_regress.tests.SessionTests.test_login_with_user', # noqa - 'test_client_regress.tests.SessionTests.test_login_without_signal', # noqa - 'test_client_regress.tests.SessionTests.test_logout', # noqa - 'test_client_regress.tests.SessionTests.test_logout_with_custom_auth_backend', # noqa - 'test_client_regress.tests.SessionTests.test_logout_with_custom_user', # noqa - 'test_client_regress.tests.SessionTests.test_logout_with_user', # noqa - 'test_client_regress.tests.SessionTests.test_logout_without_user', # noqa - 'test_client_regress.tests.SessionTests.test_session', # noqa - 'test_client_regress.tests.SessionTests.test_session_initiated', # noqa - 'timezones.tests.NewDatabaseTests.test_null_datetime', - 'transactions.tests.NonAutocommitTests.test_orm_query_after_error_and_rollback', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_empty_update_fields', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_num_queries_inheritance', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_select_related_only_interaction', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_basic', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_fk_defer', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_incorrect_params', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance_defer', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance_with_proxy_model', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_m2m', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_only_1', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_only_repeated', # noqa - 'update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_signals', # noqa - 'validation.tests.BaseModelValidationTests.test_correct_FK_value_validates', # noqa - 'validation.tests.BaseModelValidationTests.test_limited_FK_raises_error', # noqa - 'validation.tests.GenericIPAddressFieldTests.test_empty_generic_ip_passes', # noqa - 'validation.tests.GenericIPAddressFieldTests.test_v4_unpack_uniqueness_detection', # noqa - 'validation.tests.GenericIPAddressFieldTests.test_v6_uniqueness_detection', # noqa - ) diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000000..0abaf229fc --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,4 @@ +div#python2-eol { + border-color: red; + border-width: medium; +} \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000..f48471e94f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,360 @@ +# -*- coding: utf-8 -*- +# +# python-spanner-django documentation build configuration file +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# import shlex + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath("..")) + +__version__ = "" + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = "1.6.3" + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "recommonmark", +] + +# autodoc/autosummary flags +autoclass_content = "both" +autodoc_default_flags = ["members"] +autosummary_generate = True + + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# Allow markdown includes (so releases.md can include CHANGLEOG.md) +# http://www.sphinx-doc.org/en/master/markdown.html +source_parsers = {".md": "recommonmark.parser.CommonMarkParser"} + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = u"python-spanner-django" +copyright = u"2020, Google" +author = u"Google APIs" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = __version__ +# The short X.Y version. +version = ".".join(release.split(".")[0:2]) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["_build"] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "alabaster" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + "description": "Google Cloud Client Libraries for python-spanner-django", + "github_user": "googleapis", + "github_repo": "python-spanner-django", + "github_banner": True, + "font_family": "'Roboto', Georgia, sans", + "head_font_family": "'Roboto', Georgia, serif", + "code_font_family": "'Roboto Mono', 'Consolas', monospace", +} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = "python-spanner-django-doc" + +# -- Options for warnings ------------------------------------------------------ + + +suppress_warnings = [ + # Temporarily suppress this to avoid "more than one target found for + # cross-reference" warning, which are intractable for us to avoid while in + # a mono-repo. + # See https://github.com/sphinx-doc/sphinx/blob + # /2a65ffeef5c107c19084fabdd706cdff3f52d93c/sphinx/domains/python.py#L843 + "ref.python" +] + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + master_doc, + "python-spanner-django.tex", + u"python-spanner-django Documentation", + author, + "manual", + ) +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( + master_doc, + "python-spanner-django", + u"python-spanner-django Documentation", + [author], + 1, + ) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + "python-spanner-django", + u"python-spanner-django Documentation", + author, + "python-spanner-django", + "python-spanner-django Library", + "APIs", + ) +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + "python": ("http://python.readthedocs.org/en/latest/", None), + "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), + "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None), + "grpc": ("https://grpc.io/grpc/python/", None), +} + + +# Napoleon settings +napoleon_google_docstring = True +napoleon_numpy_docstring = True +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = True +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = True +napoleon_use_rtype = True diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/google/__init__.py b/google/__init__.py new file mode 100644 index 0000000000..d36bdc58d5 --- /dev/null +++ b/google/__init__.py @@ -0,0 +1,25 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Google Cloud Spanner Django API package.""" + + +try: + import pkg_resources + + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/google/cloud/__init__.py b/google/cloud/__init__.py new file mode 100644 index 0000000000..8c924dfa73 --- /dev/null +++ b/google/cloud/__init__.py @@ -0,0 +1,23 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +try: + import pkg_resources + + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/spanner_dbapi/__init__.py b/google/cloud/spanner_dbapi/__init__.py similarity index 62% rename from spanner_dbapi/__init__.py rename to google/cloud/spanner_dbapi/__init__.py index f5d349a655..3ea73790d8 100644 --- a/spanner_dbapi/__init__.py +++ b/google/cloud/spanner_dbapi/__init__.py @@ -7,16 +7,34 @@ from google.cloud import spanner_v1 as spanner from .connection import Connection + # These need to be included in the top-level package for PEP-0249 DB API v2. from .exceptions import ( - DatabaseError, DataError, Error, IntegrityError, InterfaceError, - InternalError, NotSupportedError, OperationalError, ProgrammingError, + DatabaseError, + DataError, + Error, + IntegrityError, + InterfaceError, + InternalError, + NotSupportedError, + OperationalError, + ProgrammingError, Warning, ) from .parse_utils import get_param_types from .types import ( - BINARY, DATETIME, NUMBER, ROWID, STRING, Binary, Date, DateFromTicks, Time, - TimeFromTicks, Timestamp, TimestampFromTicks, + BINARY, + DATETIME, + NUMBER, + ROWID, + STRING, + Binary, + Date, + DateFromTicks, + Time, + TimeFromTicks, + Timestamp, + TimestampFromTicks, ) from .version import google_client_info @@ -24,16 +42,21 @@ apilevel = "2.0" # Implements the Python Database API specification 2.0 version. # We accept arguments in the format '%s' aka ANSI C print codes. # as per https://www.python.org/dev/peps/pep-0249/#paramstyle -paramstyle = 'format' +paramstyle = "format" # Threads may share the module but not connections. This is a paranoid threadsafety level, # but it is necessary for starters to use when debugging failures. Eventually once transactions # are working properly, we'll update the threadsafety level. threadsafety = 1 +# [@mf2199] TODO: These are not defined but used in the tests +DEFAULT_USER_AGENT = None +TimeStampStr = None -def connect(project=None, instance=None, database=None, credentials_uri=None, user_agent=None): - """ - Connect to Cloud Spanner. + +def connect( + project=None, instance=None, database=None, credentials_uri=None, user_agent=None +): + """Connect to Cloud Spanner. Args: project: The id of a project that already exists. @@ -55,12 +78,11 @@ def connect(project=None, instance=None, database=None, credentials_uri=None, us if not database: raise Error("'database' is required.") - client_kwargs = { - 'project': project, - 'client_info': google_client_info(user_agent), - } + client_kwargs = {"project": project, "client_info": google_client_info(user_agent)} if credentials_uri: - client = spanner.Client.from_service_account_json(credentials_uri, **client_kwargs) + client = spanner.Client.from_service_account_json( + credentials_uri, **client_kwargs + ) else: client = spanner.Client(**client_kwargs) @@ -76,11 +98,33 @@ def connect(project=None, instance=None, database=None, credentials_uri=None, us __all__ = [ - 'DatabaseError', 'DataError', 'Error', 'IntegrityError', 'InterfaceError', - 'InternalError', 'NotSupportedError', 'OperationalError', 'ProgrammingError', - 'Warning', 'DEFAULT_USER_AGENT', 'apilevel', 'connect', 'paramstyle', 'threadsafety', - 'get_param_types', - 'Binary', 'Date', 'DateFromTicks', 'Time', 'TimeFromTicks', 'Timestamp', - 'TimestampFromTicks', - 'BINARY', 'STRING', 'NUMBER', 'DATETIME', 'ROWID', 'TimestampStr', + "DatabaseError", + "DataError", + "Error", + "IntegrityError", + "InterfaceError", + "InternalError", + "NotSupportedError", + "OperationalError", + "ProgrammingError", + "Warning", + "DEFAULT_USER_AGENT", + "apilevel", + "connect", + "paramstyle", + "threadsafety", + "get_param_types", + "Binary", + "Date", + "DateFromTicks", + "Time", + "TimeFromTicks", + "Timestamp", + "TimestampFromTicks", + "BINARY", + "STRING", + "NUMBER", + "DATETIME", + "ROWID", + "TimestampStr", ] diff --git a/spanner_dbapi/connection.py b/google/cloud/spanner_dbapi/connection.py similarity index 88% rename from spanner_dbapi/connection.py rename to google/cloud/spanner_dbapi/connection.py index 20b707adb0..64f8033f08 100644 --- a/spanner_dbapi/connection.py +++ b/google/cloud/spanner_dbapi/connection.py @@ -11,7 +11,7 @@ from .cursor import Cursor from .exceptions import InterfaceError -ColumnDetails = namedtuple('column_details', ['null_ok', 'spanner_type']) +ColumnDetails = namedtuple("column_details", ["null_ok", "spanner_type"]) class Connection: @@ -30,7 +30,7 @@ def __raise_if_already_closed(self): Raise an exception if attempting to use an already closed connection. """ if self._closed: - raise InterfaceError('connection already closed') + raise InterfaceError("connection already closed") def __handle_update_ddl(self, ddl_statements): """ @@ -69,14 +69,16 @@ def run_prior_DDL_statements(self): return self.__handle_update_ddl(ddl_statements) def list_tables(self): - return self.run_sql_in_snapshot(""" + return self.run_sql_in_snapshot( + """ SELECT t.table_name FROM information_schema.tables AS t WHERE t.table_catalog = '' and t.table_schema = '' - """) + """ + ) def run_sql_in_snapshot(self, sql, params=None, param_types=None): # Some SQL e.g. for INFORMATION_SCHEMA cannot be run in read-write transactions @@ -89,23 +91,22 @@ def run_sql_in_snapshot(self, sql, params=None, param_types=None): def get_table_column_schema(self, table_name): rows = self.run_sql_in_snapshot( - '''SELECT + """SELECT COLUMN_NAME, IS_NULLABLE, SPANNER_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '' AND - TABLE_NAME = @table_name''', - params={'table_name': table_name}, - param_types={'table_name': spanner.param_types.STRING}, + TABLE_NAME = @table_name""", + params={"table_name": table_name}, + param_types={"table_name": spanner.param_types.STRING}, ) column_details = {} for column_name, is_nullable, spanner_type in rows: column_details[column_name] = ColumnDetails( - null_ok=is_nullable == 'YES', - spanner_type=spanner_type, + null_ok=is_nullable == "YES", spanner_type=spanner_type ) return column_details diff --git a/spanner_dbapi/cursor.py b/google/cloud/spanner_dbapi/cursor.py similarity index 79% rename from spanner_dbapi/cursor.py rename to google/cloud/spanner_dbapi/cursor.py index d5f08c4e93..601be66e93 100644 --- a/spanner_dbapi/cursor.py +++ b/google/cloud/spanner_dbapi/cursor.py @@ -8,11 +8,19 @@ from google.cloud.spanner_v1 import param_types from .exceptions import ( - IntegrityError, InterfaceError, OperationalError, ProgrammingError, + IntegrityError, + InterfaceError, + OperationalError, + ProgrammingError, ) from .parse_utils import ( - STMT_DDL, STMT_INSERT, STMT_NON_UPDATING, classify_stmt, - ensure_where_clause, get_param_types, parse_insert, + STMT_DDL, + STMT_INSERT, + STMT_NON_UPDATING, + classify_stmt, + ensure_where_clause, + get_param_types, + parse_insert, sql_pyformat_args_to_spanner, ) from .utils import PeekIterator @@ -64,7 +72,7 @@ def execute(self, sql, args=None): self._raise_if_already_closed() if not self._connection: - raise ProgrammingError('Cursor is not connected to the database') + raise ProgrammingError("Cursor is not connected to the database") self._res = None @@ -86,23 +94,22 @@ def execute(self, sql, args=None): else: self.__handle_update(sql, args or None) except (grpc_exceptions.AlreadyExists, grpc_exceptions.FailedPrecondition) as e: - raise IntegrityError(e.details if hasattr(e, 'details') else e) + raise IntegrityError(e.details if hasattr(e, "details") else e) except grpc_exceptions.InvalidArgument as e: - raise ProgrammingError(e.details if hasattr(e, 'details') else e) + raise ProgrammingError(e.details if hasattr(e, "details") else e) except grpc_exceptions.InternalServerError as e: - raise OperationalError(e.details if hasattr(e, 'details') else e) + raise OperationalError(e.details if hasattr(e, "details") else e) def __handle_update(self, sql, params): - self._connection.in_transaction( - self.__do_execute_update, - sql, params, - ) + self._connection.in_transaction(self.__do_execute_update, sql, params) def __do_execute_update(self, transaction, sql, params, param_types=None): sql = ensure_where_clause(sql) sql, params = sql_pyformat_args_to_spanner(sql, params) - res = transaction.execute_update(sql, params=params, param_types=get_param_types(params)) + res = transaction.execute_update( + sql, params=params, param_types=get_param_types(params) + ) self._itr = None if type(res) == int: self._row_count = res @@ -125,20 +132,18 @@ def __handle_insert(self, sql, params): # transaction.execute_sql(sql, params, param_types) # which invokes more RPCs and is more costly. - if parts.get('homogenous'): + if parts.get("homogenous"): # The common case of multiple values being passed in # non-complex pyformat args and need to be uploaded in one RPC. return self._connection.in_transaction( - self.__do_execute_insert_homogenous, - parts, + self.__do_execute_insert_homogenous, parts ) else: # All the other cases that are esoteric and need # transaction.execute_sql - sql_params_list = parts.get('sql_params_list') + sql_params_list = parts.get("sql_params_list") return self._connection.in_transaction( - self.__do_execute_insert_heterogenous, - sql_params_list, + self.__do_execute_insert_heterogenous, sql_params_list ) def __do_execute_insert_heterogenous(self, transaction, sql_params_list): @@ -152,9 +157,9 @@ def __do_execute_insert_heterogenous(self, transaction, sql_params_list): def __do_execute_insert_homogenous(self, transaction, parts): # Perform an insert in one shot. - table = parts.get('table') - columns = parts.get('columns') - values = parts.get('values') + table = parts.get("table") + columns = parts.get("columns") + values = parts.get("values") return transaction.insert(table, columns, values) def __handle_DQL(self, sql, params): @@ -162,7 +167,9 @@ def __handle_DQL(self, sql, params): # Reference # https://googleapis.dev/python/spanner/latest/session-api.html#google.cloud.spanner_v1.session.Session.execute_sql sql, params = sql_pyformat_args_to_spanner(sql, params) - res = snapshot.execute_sql(sql, params=params, param_types=get_param_types(params)) + res = snapshot.execute_sql( + sql, params=params, param_types=get_param_types(params) + ) if type(res) == int: self._row_count = res self._itr = None @@ -221,7 +228,7 @@ def _raise_if_already_closed(self): Raise an exception if attempting to use an already closed connection. """ if self._closed: - raise InterfaceError('cursor already closed') + raise InterfaceError("cursor already closed") def close(self): self.__clear() @@ -229,19 +236,19 @@ def close(self): def executemany(self, operation, seq_of_params): if not self._connection: - raise ProgrammingError('Cursor is not connected to the database') + raise ProgrammingError("Cursor is not connected to the database") for params in seq_of_params: self.execute(operation, params) def __next__(self): if self._itr is None: - raise ProgrammingError('no results to return') + raise ProgrammingError("no results to return") return next(self._itr) def __iter__(self): if self._itr is None: - raise ProgrammingError('no results to return') + raise ProgrammingError("no results to return") return self._itr def fetchone(self): @@ -289,10 +296,10 @@ def lastrowid(self): return None def setinputsizes(sizes): - raise ProgrammingError('Unimplemented') + raise ProgrammingError("Unimplemented") def setoutputsize(size, column=None): - raise ProgrammingError('Unimplemented') + raise ProgrammingError("Unimplemented") def _run_prior_DDL_statements(self): return self._connection.run_prior_DDL_statements() @@ -308,8 +315,16 @@ def get_table_column_schema(self, table_name): class Column: - def __init__(self, name, type_code, display_size=None, internal_size=None, - precision=None, scale=None, null_ok=False): + def __init__( + self, + name, + type_code, + display_size=None, + internal_size=None, + precision=None, + scale=None, + null_ok=False, + ): self.name = name self.type_code = type_code self.display_size = display_size @@ -338,14 +353,24 @@ def __getitem__(self, index): return self.null_ok def __str__(self): - rstr = ', '.join([field for field in [ - "name='%s'" % self.name, - "type_code=%d" % self.type_code, - None if not self.display_size else "display_size=%d" % self.display_size, - None if not self.internal_size else "internal_size=%d" % self.internal_size, - None if not self.precision else "precision='%s'" % self.precision, - None if not self.scale else "scale='%s'" % self.scale, - None if not self.null_ok else "null_ok='%s'" % self.null_ok, - ] if field]) - - return 'Column(%s)' % rstr + rstr = ", ".join( + [ + field + for field in [ + "name='%s'" % self.name, + "type_code=%d" % self.type_code, + None + if not self.display_size + else "display_size=%d" % self.display_size, + None + if not self.internal_size + else "internal_size=%d" % self.internal_size, + None if not self.precision else "precision='%s'" % self.precision, + None if not self.scale else "scale='%s'" % self.scale, + None if not self.null_ok else "null_ok='%s'" % self.null_ok, + ] + if field + ] + ) + + return "Column(%s)" % rstr diff --git a/spanner_dbapi/exceptions.py b/google/cloud/spanner_dbapi/exceptions.py similarity index 100% rename from spanner_dbapi/exceptions.py rename to google/cloud/spanner_dbapi/exceptions.py diff --git a/spanner_dbapi/parse_utils.py b/google/cloud/spanner_dbapi/parse_utils.py similarity index 72% rename from spanner_dbapi/parse_utils.py rename to google/cloud/spanner_dbapi/parse_utils.py index c6299a3c87..46e9f0c9df 100644 --- a/spanner_dbapi/parse_utils.py +++ b/google/cloud/spanner_dbapi/parse_utils.py @@ -17,20 +17,20 @@ from .types import DateStr, TimestampStr from .utils import sanitize_literals_for_upload -STMT_DDL = 'DDL' -STMT_NON_UPDATING = 'NON_UPDATING' -STMT_UPDATING = 'UPDATING' -STMT_INSERT = 'INSERT' +STMT_DDL = "DDL" +STMT_NON_UPDATING = "NON_UPDATING" +STMT_UPDATING = "UPDATING" +STMT_INSERT = "INSERT" # Heuristic for identifying statements that don't need to be run as updates. -re_NON_UPDATE = re.compile(r'^\s*(SELECT)', re.IGNORECASE) +re_NON_UPDATE = re.compile(r"^\s*(SELECT)", re.IGNORECASE) -re_WITH = re.compile(r'^\s*(WITH)', re.IGNORECASE) +re_WITH = re.compile(r"^\s*(WITH)", re.IGNORECASE) # DDL statements follow https://cloud.google.com/spanner/docs/data-definition-language -re_DDL = re.compile(r'^\s*(CREATE|ALTER|DROP)', re.IGNORECASE | re.DOTALL) +re_DDL = re.compile(r"^\s*(CREATE|ALTER|DROP)", re.IGNORECASE | re.DOTALL) -re_IS_INSERT = re.compile(r'^\s*(INSERT)', re.IGNORECASE | re.DOTALL) +re_IS_INSERT = re.compile(r"^\s*(INSERT)", re.IGNORECASE | re.DOTALL) def classify_stmt(sql): @@ -53,18 +53,15 @@ def classify_stmt(sql): # Only match the `INSERT INTO (columns...) # otherwise the rest of the statement could be a complex # operation. - r'^\s*INSERT INTO (?P[^\s\(\)]+)\s*\((?P[^\(\)]+)\)', + r"^\s*INSERT INTO (?P[^\s\(\)]+)\s*\((?P[^\(\)]+)\)", re.IGNORECASE | re.DOTALL, ) -re_VALUES_TILL_END = re.compile( - r'VALUES\s*\(.+$', - re.IGNORECASE | re.DOTALL, -) +re_VALUES_TILL_END = re.compile(r"VALUES\s*\(.+$", re.IGNORECASE | re.DOTALL) re_VALUES_PYFORMAT = re.compile( # To match: (%s, %s,....%s) - r'(\(\s*%s[^\(\)]+\))', + r"(\(\s*%s[^\(\)]+\))", re.DOTALL, ) @@ -74,7 +71,7 @@ def strip_backticks(name): Strip backticks off of quoted names For example, '`no`' (a Spanner reserved word) becomes 'no'. """ - has_quotes = name.startswith('`') and name.endswith('`') + has_quotes = name.startswith("`") and name.endswith("`") return name[1:-1] if has_quotes else name @@ -139,30 +136,30 @@ def parse_insert(insert_sql, params): match = re_INSERT.search(insert_sql) if not match: - raise ProgrammingError('Could not parse an INSERT statement from %s' % insert_sql) + raise ProgrammingError( + "Could not parse an INSERT statement from %s" % insert_sql + ) after_values_sql = re_VALUES_TILL_END.findall(insert_sql) if not after_values_sql: # Case b) insert_sql = sanitize_literals_for_upload(insert_sql) - return { - 'sql_params_list': [(insert_sql, None,)], - } + return {"sql_params_list": [(insert_sql, None)]} if not params: # Case a) perhaps? # Check if any %s exists. - pyformat_str_count = after_values_sql.count('%s') + pyformat_str_count = after_values_sql.count("%s") if pyformat_str_count > 0: - raise ProgrammingError('no params yet there are %d "%s" tokens' % pyformat_str_count) + raise ProgrammingError( + 'no params yet there are %d "%s" tokens' % pyformat_str_count + ) insert_sql = sanitize_literals_for_upload(insert_sql) # Confirmed case of: # SQL: INSERT INTO T (a1, a2) VALUES (1, 2) # Params: None - return { - 'sql_params_list': [(insert_sql, None,)], - } + return {"sql_params_list": [(insert_sql, None)]} values_str = after_values_sql[0] _, values = parse_values(values_str) @@ -171,22 +168,21 @@ def parse_insert(insert_sql, params): # Case c) columns = [ - strip_backticks(mi.strip()) - for mi in match.group('columns').split(',') + strip_backticks(mi.strip()) for mi in match.group("columns").split(",") ] sql_params_list = [] - insert_sql_preamble = 'INSERT INTO %s (%s) VALUES %s' % ( - match.group('table_name'), match.group('columns'), values.argv[0], + insert_sql_preamble = "INSERT INTO %s (%s) VALUES %s" % ( + match.group("table_name"), + match.group("columns"), + values.argv[0], ) values_pyformat = [str(arg) for arg in values.argv] rows_list = rows_for_insert_or_update(columns, params, values_pyformat) insert_sql_preamble = sanitize_literals_for_upload(insert_sql_preamble) for row in rows_list: - sql_params_list.append((insert_sql_preamble, row,)) + sql_params_list.append((insert_sql_preamble, row)) - return { - 'sql_params_list': sql_params_list, - } + return {"sql_params_list": sql_params_list} # Case d) # insert_sql is of the form: @@ -194,10 +190,11 @@ def parse_insert(insert_sql, params): # Sanity check: # length(all_args) == len(params) - args_len = reduce(lambda a, b: a+b, [len(arg) for arg in values.argv]) + args_len = reduce(lambda a, b: a + b, [len(arg) for arg in values.argv]) if args_len != len(params): - raise ProgrammingError('Invalid length: VALUES(...) len: %d != len(params): %d' % ( - args_len, len(params)), + raise ProgrammingError( + "Invalid length: VALUES(...) len: %d != len(params): %d" + % (args_len, len(params)) ) trim_index = insert_sql.find(values_str) @@ -205,14 +202,12 @@ def parse_insert(insert_sql, params): sql_param_tuples = [] for token_arg in values.argv: - row_sql = before_values_sql + ' VALUES%s' % token_arg + row_sql = before_values_sql + " VALUES%s" % token_arg row_sql = sanitize_literals_for_upload(row_sql) - row_params, params = tuple(params[0:len(token_arg)]), params[len(token_arg):] - sql_param_tuples.append((row_sql, row_params,)) + row_params, params = tuple(params[0 : len(token_arg)]), params[len(token_arg) :] + sql_param_tuples.append((row_sql, row_params)) - return { - 'sql_params_list': sql_param_tuples, - } + return {"sql_params_list": sql_param_tuples} def rows_for_insert_or_update(columns, params, pyformat_args=None): @@ -247,8 +242,10 @@ def rows_for_insert_or_update(columns, params, pyformat_args=None): columns_len = len(columns) for param in params: if columns_len != len(param): - raise Error('\nlen(`%s`)=%d\n!=\ncolum_len(`%s`)=%d' % ( - param, len(param), columns, columns_len)) + raise Error( + "\nlen(`%s`)=%d\n!=\ncolum_len(`%s`)=%d" + % (param, len(param), columns, columns_len) + ) return params else: # The case with Params B: [1, 2, 3] @@ -267,12 +264,14 @@ def rows_for_insert_or_update(columns, params, pyformat_args=None): # Sanity check 1: all the pyformat_values should have the exact same length. first, rest = pyformat_args[0], pyformat_args[1:] - n_stride = first.count('%s') + n_stride = first.count("%s") for pyfmt_value in rest: - n = pyfmt_value.count('%s') + n = pyfmt_value.count("%s") if n_stride != n: - raise Error('\nlen(`%s`)=%d\n!=\nlen(`%s`)=%d' % ( - first, n_stride, pyfmt_value, n)) + raise Error( + "\nlen(`%s`)=%d\n!=\nlen(`%s`)=%d" + % (first, n_stride, pyfmt_value, n) + ) # Sanity check 2: len(params) MUST be a multiple of n_stride aka len(count of %s). # so that we can properly group for example: @@ -283,20 +282,21 @@ def rows_for_insert_or_update(columns, params, pyformat_args=None): # into # [(1, 2, 3), (4, 5, 6), (7, 8, 9)] if (len(params) % n_stride) != 0: - raise ProgrammingError('Invalid length: len(params)=%d MUST be a multiple of len(pyformat_args)=%d' % ( - len(params), n_stride), + raise ProgrammingError( + "Invalid length: len(params)=%d MUST be a multiple of len(pyformat_args)=%d" + % (len(params), n_stride) ) # Now chop up the strides. strides = [] for step in range(0, len(params), n_stride): - stride = tuple(params[step:step+n_stride:]) + stride = tuple(params[step : step + n_stride :]) strides.append(stride) return strides -re_PYFORMAT = re.compile(r'(%s|%\([^\(\)]+\)s)+', re.DOTALL) +re_PYFORMAT = re.compile(r"(%s|%\([^\(\)]+\)s)+", re.DOTALL) def sql_pyformat_args_to_spanner(sql, params): @@ -332,8 +332,10 @@ def sql_pyformat_args_to_spanner(sql, params): n_matches = len(found_pyformat_placeholders) if n_matches != n_params: raise Error( - 'pyformat_args mismatch\ngot %d args from %s\n' - 'want %d args in %s' % (n_matches, found_pyformat_placeholders, n_params, params)) + "pyformat_args mismatch\ngot %d args from %s\n" + "want %d args in %s" + % (n_matches, found_pyformat_placeholders, n_params, params) + ) if len(params) == 0: return sanitize_literals_for_upload(sql), params @@ -345,8 +347,8 @@ def sql_pyformat_args_to_spanner(sql, params): # Params: ('a', 23, '888***') # Case b) Params is a dict and the matches are %(value)s' for i, pyfmt in enumerate(found_pyformat_placeholders): - key = 'a%d' % i - sql = sql.replace(pyfmt, '@'+key, 1) + key = "a%d" % i + sql = sql.replace(pyfmt, "@" + key, 1) if params_is_dict: # The '%(key)s' case, so interpolate it. resolved_value = pyfmt % params @@ -379,9 +381,9 @@ def get_param_types(params): param_types[key] = spanner.param_types.FLOAT64 elif isinstance(value, int): param_types[key] = spanner.param_types.INT64 - elif isinstance(value, (TimestampStr, datetime.datetime,)): + elif isinstance(value, (TimestampStr, datetime.datetime)): param_types[key] = spanner.param_types.TIMESTAMP - elif isinstance(value, (DateStr, datetime.date,)): + elif isinstance(value, (DateStr, datetime.date)): param_types[key] = spanner.param_types.DATE elif isinstance(value, str): param_types[key] = spanner.param_types.STRING @@ -397,106 +399,106 @@ def ensure_where_clause(sql): """ if any(isinstance(token, sqlparse.sql.Where) for token in sqlparse.parse(sql)[0]): return sql - return sql + ' WHERE 1=1' + return sql + " WHERE 1=1" SPANNER_RESERVED_KEYWORDS = { - 'ALL', - 'AND', - 'ANY', - 'ARRAY', - 'AS', - 'ASC', - 'ASSERT_ROWS_MODIFIED', - 'AT', - 'BETWEEN', - 'BY', - 'CASE', - 'CAST', - 'COLLATE', - 'CONTAINS', - 'CREATE', - 'CROSS', - 'CUBE', - 'CURRENT', - 'DEFAULT', - 'DEFINE', - 'DESC', - 'DISTINCT', - 'DROP', - 'ELSE', - 'END', - 'ENUM', - 'ESCAPE', - 'EXCEPT', - 'EXCLUDE', - 'EXISTS', - 'EXTRACT', - 'FALSE', - 'FETCH', - 'FOLLOWING', - 'FOR', - 'FROM', - 'FULL', - 'GROUP', - 'GROUPING', - 'GROUPS', - 'HASH', - 'HAVING', - 'IF', - 'IGNORE', - 'IN', - 'INNER', - 'INTERSECT', - 'INTERVAL', - 'INTO', - 'IS', - 'JOIN', - 'LATERAL', - 'LEFT', - 'LIKE', - 'LIMIT', - 'LOOKUP', - 'MERGE', - 'NATURAL', - 'NEW', - 'NO', - 'NOT', - 'NULL', - 'NULLS', - 'OF', - 'ON', - 'OR', - 'ORDER', - 'OUTER', - 'OVER', - 'PARTITION', - 'PRECEDING', - 'PROTO', - 'RANGE', - 'RECURSIVE', - 'RESPECT', - 'RIGHT', - 'ROLLUP', - 'ROWS', - 'SELECT', - 'SET', - 'SOME', - 'STRUCT', - 'TABLESAMPLE', - 'THEN', - 'TO', - 'TREAT', - 'TRUE', - 'UNBOUNDED', - 'UNION', - 'UNNEST', - 'USING', - 'WHEN', - 'WHERE', - 'WINDOW', - 'WITH', - 'WITHIN', + "ALL", + "AND", + "ANY", + "ARRAY", + "AS", + "ASC", + "ASSERT_ROWS_MODIFIED", + "AT", + "BETWEEN", + "BY", + "CASE", + "CAST", + "COLLATE", + "CONTAINS", + "CREATE", + "CROSS", + "CUBE", + "CURRENT", + "DEFAULT", + "DEFINE", + "DESC", + "DISTINCT", + "DROP", + "ELSE", + "END", + "ENUM", + "ESCAPE", + "EXCEPT", + "EXCLUDE", + "EXISTS", + "EXTRACT", + "FALSE", + "FETCH", + "FOLLOWING", + "FOR", + "FROM", + "FULL", + "GROUP", + "GROUPING", + "GROUPS", + "HASH", + "HAVING", + "IF", + "IGNORE", + "IN", + "INNER", + "INTERSECT", + "INTERVAL", + "INTO", + "IS", + "JOIN", + "LATERAL", + "LEFT", + "LIKE", + "LIMIT", + "LOOKUP", + "MERGE", + "NATURAL", + "NEW", + "NO", + "NOT", + "NULL", + "NULLS", + "OF", + "ON", + "OR", + "ORDER", + "OUTER", + "OVER", + "PARTITION", + "PRECEDING", + "PROTO", + "RANGE", + "RECURSIVE", + "RESPECT", + "RIGHT", + "ROLLUP", + "ROWS", + "SELECT", + "SET", + "SOME", + "STRUCT", + "TABLESAMPLE", + "THEN", + "TO", + "TREAT", + "TRUE", + "UNBOUNDED", + "UNION", + "UNNEST", + "USING", + "WHEN", + "WHERE", + "WINDOW", + "WITH", + "WITHIN", } @@ -505,6 +507,6 @@ def escape_name(name): Escape name by applying backticks to value that either contain '-' or are any of Cloud Spanner's reserved keywords. """ - if '-' in name or ' ' in name or name.upper() in SPANNER_RESERVED_KEYWORDS: - return '`' + name + '`' + if "-" in name or " " in name or name.upper() in SPANNER_RESERVED_KEYWORDS: + return "`" + name + "`" return name diff --git a/spanner_dbapi/parser.py b/google/cloud/spanner_dbapi/parser.py similarity index 71% rename from spanner_dbapi/parser.py rename to google/cloud/spanner_dbapi/parser.py index 8518a256b2..b5ae720e05 100644 --- a/spanner_dbapi/parser.py +++ b/google/cloud/spanner_dbapi/parser.py @@ -27,11 +27,11 @@ from .exceptions import ProgrammingError -ARGS = 'ARGS' -EXPR = 'EXPR' -FUNC = 'FUNC' -TERMINAL = 'TERMINAL' -VALUES = 'VALUES' +ARGS = "ARGS" +EXPR = "EXPR" +FUNC = "FUNC" +TERMINAL = "TERMINAL" +VALUES = "VALUES" class func: @@ -40,7 +40,7 @@ def __init__(self, func_name, args): self.args = args def __str__(self): - return '%s%s' % (self.name, self.args) + return "%s%s" % (self.name, self.args) def __repr__(self): return self.__str__() @@ -64,6 +64,7 @@ class terminal(str): """ terminal represents the unit symbol that can be part of a SQL values clause. """ + pass @@ -72,7 +73,7 @@ def __init__(self, argv): self.argv = argv def __str__(self): - return '(' + ', '.join([str(arg) for arg in self.argv]) + ')' + return "(" + ", ".join([str(arg) for arg in self.argv]) + ")" def __repr__(self): return self.__str__() @@ -136,59 +137,63 @@ def all_have_same_argc(self): class values(a_args): def __str__(self): - return 'VALUES%s' % super().__str__() + return "VALUES%s" % super().__str__() def parse_values(stmt): return expect(stmt, VALUES) -pyfmt_str = terminal('%s') +pyfmt_str = terminal("%s") def expect(word, token): - word = word.strip(' ') + word = word.strip(" ") if token == VALUES: - if not word.startswith('VALUES'): - raise ProgrammingError('VALUES: `%s` does not start with VALUES' % word) - word = word[len('VALUES'):].strip(' ') + if not word.startswith("VALUES"): + raise ProgrammingError("VALUES: `%s` does not start with VALUES" % word) + word = word[len("VALUES") :].strip(" ") all_args = [] while word: - word = word.strip(' ') + word = word.strip(" ") word, arg = expect(word, ARGS) all_args.append(arg) - word = word.strip(' ') - if word == '': + word = word.strip(" ") + if word == "": break - elif word[0] != ',': - raise ProgrammingError('VALUES: expected `,` got %s in %s' % (word[0], word)) + elif word[0] != ",": + raise ProgrammingError( + "VALUES: expected `,` got %s in %s" % (word[0], word) + ) else: word = word[1:] - return '', values(all_args) + return "", values(all_args) elif token == TERMINAL: - word = word.strip(' ') - if word != '%s': - raise ProgrammingError('TERMINAL: `%s` is not %%s' % word) - return '', pyfmt_str + word = word.strip(" ") + if word != "%s": + raise ProgrammingError("TERMINAL: `%s` is not %%s" % word) + return "", pyfmt_str elif token == FUNC: - begins_with_letter = word and (word[0].isalpha() or word[0] == '_') + begins_with_letter = word and (word[0].isalpha() or word[0] == "_") if not begins_with_letter: - raise ProgrammingError('FUNC: `%s` does not begin with `a-zA-z` nor a `_`' % word) + raise ProgrammingError( + "FUNC: `%s` does not begin with `a-zA-z` nor a `_`" % word + ) rest = word[1:] end = 0 for ch in rest: - if ch.isalnum() or ch == '_': + if ch.isalnum() or ch == "_": end += 1 else: break - func_name, rest = word[:end+1], word[end+1:].strip(' ') + func_name, rest = word[: end + 1], word[end + 1 :].strip(" ") word, args = expect(rest, ARGS) return word, func(func_name, args) @@ -199,47 +204,47 @@ def expect(word, token): # (%s, %s...) # (FUNC, %s...) # (%s, %s...) - if not (word and word[0] == '('): - raise ProgrammingError('ARGS: supposed to begin with `(` in `%s`' % (word)) + if not (word and word[0] == "("): + raise ProgrammingError("ARGS: supposed to begin with `(` in `%s`" % (word)) word = word[1:] terms = [] while True: - word = word.strip(' ') + word = word.strip(" ") if not word: break - elif word == '%s': + elif word == "%s": terms.append(pyfmt_str) - word = '' - elif word.startswith(')'): + word = "" + elif word.startswith(")"): break - elif not word.startswith('%s'): + elif not word.startswith("%s"): word, parsed = expect(word, FUNC) terms.append(parsed) else: terms.append(pyfmt_str) - word = word[2:].strip(' ') + word = word[2:].strip(" ") - if word and word[0] == ',': + if word and word[0] == ",": word = word[1:] - if (not word) or word[0] != ')': - raise ProgrammingError('ARGS: supposed to end with `)` in `%s`' % (word)) + if (not word) or word[0] != ")": + raise ProgrammingError("ARGS: supposed to end with `)` in `%s`" % (word)) word = word[1:] return word, a_args(terms) elif token == EXPR: - if word == '%s': + if word == "%s": # Terminal symbol. - return '', (pyfmt_str) + return "", (pyfmt_str) # Otherwise we expect a function. return expect(word, FUNC) else: - raise ProgrammingError('Unknown token `%s`' % token) + raise ProgrammingError("Unknown token `%s`" % token) def as_values(values_stmt): diff --git a/spanner_dbapi/types.py b/google/cloud/spanner_dbapi/types.py similarity index 99% rename from spanner_dbapi/types.py rename to google/cloud/spanner_dbapi/types.py index 8535a63564..2299f279ba 100644 --- a/spanner_dbapi/types.py +++ b/google/cloud/spanner_dbapi/types.py @@ -47,6 +47,7 @@ class BINARY: """ This object describes (long) binary columns in a database (e.g. LONG, RAW, BLOBS). """ + # TODO: Implement me. pass @@ -55,6 +56,7 @@ class STRING: """ This object describes columns in a database that are string-based (e.g. CHAR). """ + # TODO: Implement me. pass @@ -63,6 +65,7 @@ class NUMBER: """ This object describes numeric columns in a database. """ + # TODO: Implement me. pass @@ -71,6 +74,7 @@ class DATETIME: """ This object describes date/time columns in a database. """ + # TODO: Implement me. pass @@ -79,6 +83,7 @@ class ROWID: """ This object describes the "Row ID" column in a database. """ + # TODO: Implement me. pass @@ -90,6 +95,7 @@ class TimestampStr(str): queries, it'll help differentiate between normal strings and the case of types that should be TIMESTAMP. """ + pass @@ -100,4 +106,5 @@ class DateStr(str): queries, it'll help differentiate between normal strings and the case of types that should be DATE. """ + pass diff --git a/spanner_dbapi/utils.py b/google/cloud/spanner_dbapi/utils.py similarity index 88% rename from spanner_dbapi/utils.py rename to google/cloud/spanner_dbapi/utils.py index 4b265ac8c2..f4769e80a4 100644 --- a/spanner_dbapi/utils.py +++ b/google/cloud/spanner_dbapi/utils.py @@ -15,6 +15,7 @@ class PeekIterator: If next's result is an instance of list, it'll be converted into a tuple to conform with DBAPI v2's sequence expectations. """ + def __init__(self, source): itr_src = iter(source) @@ -46,7 +47,7 @@ def __iter__(self): return self -re_UNICODE_POINTS = re.compile(r'([^\s]*[\u0080-\uFFFF]+[^\s]*)') +re_UNICODE_POINTS = re.compile(r"([^\s]*[\u0080-\uFFFF]+[^\s]*)") def backtick_unicode(sql): @@ -59,14 +60,14 @@ def backtick_unicode(sql): last_end = 0 for match in matches: start, end = match.span() - if sql[start] != '`' and sql[end-1] != '`': - segments.append(sql[last_end:start] + '`' + sql[start:end] + '`') + if sql[start] != "`" and sql[end - 1] != "`": + segments.append(sql[last_end:start] + "`" + sql[start:end] + "`") else: segments.append(sql[last_end:end]) last_end = end - return ''.join(segments) + return "".join(segments) def sanitize_literals_for_upload(s): @@ -77,4 +78,4 @@ def sanitize_literals_for_upload(s): uses placeholders like @a0 and doesn't expect percent signs to be escaped. 2. Quote words containing non-ASCII, with backticks, for example föö to `föö`. """ - return backtick_unicode(s.replace('%%', '%')) + return backtick_unicode(s.replace("%%", "%")) diff --git a/spanner_dbapi/version.py b/google/cloud/spanner_dbapi/version.py similarity index 81% rename from spanner_dbapi/version.py rename to google/cloud/spanner_dbapi/version.py index a209ae438a..a9afaecc65 100644 --- a/spanner_dbapi/version.py +++ b/google/cloud/spanner_dbapi/version.py @@ -8,8 +8,8 @@ from google.api_core.gapic_v1.client_info import ClientInfo -VERSION = '0.0.1' -DEFAULT_USER_AGENT = 'django_spanner/' + VERSION +VERSION = "0.0.1" +DEFAULT_USER_AGENT = "spanner_django/" + VERSION vers = sys.version_info @@ -22,5 +22,5 @@ def google_client_info(user_agent=None): return ClientInfo( user_agent=user_agent or DEFAULT_USER_AGENT, - python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0), + python_version="%d.%d.%d" % (vers.major, vers.minor, vers.micro or 0), ) diff --git a/django_spanner/__init__.py b/google/cloud/spanner_django/__init__.py similarity index 94% rename from django_spanner/__init__.py rename to google/cloud/spanner_django/__init__.py index b08f08f460..d1cba8e103 100644 --- a/django_spanner/__init__.py +++ b/google/cloud/spanner_django/__init__.py @@ -5,12 +5,14 @@ # https://developers.google.com/open-source/licenses/bsd import datetime + # Monkey-patch AutoField to generate a random value since Cloud Spanner can't # do that. from uuid import uuid4 import pkg_resources from django.db.models.fields import AutoField, Field + # Monkey-patch google.DatetimeWithNanoseconds's __eq__ compare against datetime.datetime. from google.api_core.datetime_helpers import DatetimeWithNanoseconds @@ -33,14 +35,14 @@ def gen_rand_int64(): def autofield_init(self, *args, **kwargs): - kwargs['blank'] = True + kwargs["blank"] = True Field.__init__(self, *args, **kwargs) self.default = gen_rand_int64 AutoField.__init__ = autofield_init -old_datetimewithnanoseconds_eq = getattr(DatetimeWithNanoseconds, '__eq__', None) +old_datetimewithnanoseconds_eq = getattr(DatetimeWithNanoseconds, "__eq__", None) def datetimewithnanoseconds_eq(self, other): @@ -62,12 +64,13 @@ def datetimewithnanoseconds_eq(self, other): DatetimeWithNanoseconds.__eq__ = datetimewithnanoseconds_eq # Sanity check here since tests can't easily be run for this file: -if __name__ == '__main__': +if __name__ == "__main__": from django.utils import timezone + UTC = timezone.utc dt = datetime.datetime(2020, 1, 10, 2, 44, 57, 999, UTC) dtns = DatetimeWithNanoseconds(2020, 1, 10, 2, 44, 57, 999, UTC) equal = dtns == dt if not equal: - raise Exception('%s\n!=\n%s' % (dtns, dt)) + raise Exception("%s\n!=\n%s" % (dtns, dt)) diff --git a/django_spanner/base.py b/google/cloud/spanner_django/base.py similarity index 54% rename from django_spanner/base.py rename to google/cloud/spanner_django/base.py index 70f5b1ba39..9c7e17a464 100644 --- a/django_spanner/base.py +++ b/google/cloud/spanner_django/base.py @@ -4,9 +4,8 @@ # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd -import spanner_dbapi as Database from django.db.backends.base.base import BaseDatabaseWrapper -from google.cloud import spanner_v1 as spanner +from google.cloud import spanner_v1 as spanner, spanner_dbapi as Database from .client import DatabaseClient from .creation import DatabaseCreation @@ -18,62 +17,62 @@ class DatabaseWrapper(BaseDatabaseWrapper): - vendor = 'spanner' - display_name = 'Cloud Spanner' + vendor = "spanner" + display_name = "Cloud Spanner" # Mapping of Field objects to their column types. # https://cloud.google.com/spanner/docs/data-types#date-type data_types = { - 'AutoField': 'INT64', - 'BigAutoField': 'INT64', - 'BinaryField': 'BYTES(MAX)', - 'BooleanField': 'BOOL', - 'CharField': 'STRING(%(max_length)s)', - 'DateField': 'DATE', - 'DateTimeField': 'TIMESTAMP', - 'DecimalField': 'FLOAT64', - 'DurationField': 'INT64', - 'EmailField': 'STRING(%(max_length)s)', - 'FileField': 'STRING(%(max_length)s)', - 'FilePathField': 'STRING(%(max_length)s)', - 'FloatField': 'FLOAT64', - 'IntegerField': 'INT64', - 'BigIntegerField': 'INT64', - 'IPAddressField': 'STRING(15)', - 'GenericIPAddressField': 'STRING(39)', - 'NullBooleanField': 'BOOL', - 'OneToOneField': 'INT64', - 'PositiveIntegerField': 'INT64', - 'PositiveSmallIntegerField': 'INT64', - 'SlugField': 'STRING(%(max_length)s)', - 'SmallAutoField': 'INT64', - 'SmallIntegerField': 'INT64', - 'TextField': 'STRING(MAX)', - 'TimeField': 'TIMESTAMP', - 'UUIDField': 'STRING(32)', + "AutoField": "INT64", + "BigAutoField": "INT64", + "BinaryField": "BYTES(MAX)", + "BooleanField": "BOOL", + "CharField": "STRING(%(max_length)s)", + "DateField": "DATE", + "DateTimeField": "TIMESTAMP", + "DecimalField": "FLOAT64", + "DurationField": "INT64", + "EmailField": "STRING(%(max_length)s)", + "FileField": "STRING(%(max_length)s)", + "FilePathField": "STRING(%(max_length)s)", + "FloatField": "FLOAT64", + "IntegerField": "INT64", + "BigIntegerField": "INT64", + "IPAddressField": "STRING(15)", + "GenericIPAddressField": "STRING(39)", + "NullBooleanField": "BOOL", + "OneToOneField": "INT64", + "PositiveIntegerField": "INT64", + "PositiveSmallIntegerField": "INT64", + "SlugField": "STRING(%(max_length)s)", + "SmallAutoField": "INT64", + "SmallIntegerField": "INT64", + "TextField": "STRING(MAX)", + "TimeField": "TIMESTAMP", + "UUIDField": "STRING(32)", } operators = { - 'exact': '= %s', - 'iexact': 'REGEXP_CONTAINS(%s, %%%%s)', + "exact": "= %s", + "iexact": "REGEXP_CONTAINS(%s, %%%%s)", # contains uses REGEXP_CONTAINS instead of LIKE to allow # DatabaseOperations.prep_for_like_query() to do regular expression # escaping. prep_for_like_query() is called for all the lookups that # use REGEXP_CONTAINS except regex/iregex (see # django.db.models.lookups.PatternLookup). - 'contains': 'REGEXP_CONTAINS(%s, %%%%s)', - 'icontains': 'REGEXP_CONTAINS(%s, %%%%s)', - 'gt': '> %s', - 'gte': '>= %s', - 'lt': '< %s', - 'lte': '<= %s', + "contains": "REGEXP_CONTAINS(%s, %%%%s)", + "icontains": "REGEXP_CONTAINS(%s, %%%%s)", + "gt": "> %s", + "gte": ">= %s", + "lt": "< %s", + "lte": "<= %s", # Using REGEXP_CONTAINS instead of STARTS_WITH and ENDS_WITH for the # same reasoning as described above for 'contains'. - 'startswith': 'REGEXP_CONTAINS(%s, %%%%s)', - 'endswith': 'REGEXP_CONTAINS(%s, %%%%s)', - 'istartswith': 'REGEXP_CONTAINS(%s, %%%%s)', - 'iendswith': 'REGEXP_CONTAINS(%s, %%%%s)', - 'regex': 'REGEXP_CONTAINS(%s, %%%%s)', - 'iregex': 'REGEXP_CONTAINS(%s, %%%%s)', + "startswith": "REGEXP_CONTAINS(%s, %%%%s)", + "endswith": "REGEXP_CONTAINS(%s, %%%%s)", + "istartswith": "REGEXP_CONTAINS(%s, %%%%s)", + "iendswith": "REGEXP_CONTAINS(%s, %%%%s)", + "regex": "REGEXP_CONTAINS(%s, %%%%s)", + "iregex": "REGEXP_CONTAINS(%s, %%%%s)", } # pattern_esc is used to generate SQL pattern lookup clauses when the @@ -81,16 +80,18 @@ class DatabaseWrapper(BaseDatabaseWrapper): # expression or the result of a bilateral transformation). In those cases, # special characters for REGEXP_CONTAINS operators (e.g. \, *, _) must be # escaped on database side. - pattern_esc = r'REPLACE(REPLACE(REPLACE({}, "\\", "\\\\"), "%%", r"\%%"), "_", r"\_")' + pattern_esc = ( + r'REPLACE(REPLACE(REPLACE({}, "\\", "\\\\"), "%%", r"\%%"), "_", r"\_")' + ) # These are all no-ops in favor of using REGEXP_CONTAINS in the customized # lookups. pattern_ops = { - 'contains': '', - 'icontains': '', - 'startswith': '', - 'istartswith': '', - 'endswith': '', - 'iendswith': '', + "contains": "", + "icontains": "", + "startswith": "", + "istartswith": "", + "endswith": "", + "iendswith": "", } Database = Database @@ -104,7 +105,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): @property def instance(self): - return spanner.Client().instance(self.settings_dict['INSTANCE']) + return spanner.Client().instance(self.settings_dict["INSTANCE"]) @property def _nodb_connection(self): @@ -112,11 +113,11 @@ def _nodb_connection(self): def get_connection_params(self): return { - 'project': self.settings_dict['PROJECT'], - 'instance': self.settings_dict['INSTANCE'], - 'database': self.settings_dict['NAME'], - 'user_agent': 'django_spanner/0.0.1', - **self.settings_dict['OPTIONS'], + "project": self.settings_dict["PROJECT"], + "instance": self.settings_dict["INSTANCE"], + "database": self.settings_dict["NAME"], + "user_agent": "spanner_django/0.0.1", + **self.settings_dict["OPTIONS"], } def get_new_connection(self, conn_params): @@ -137,7 +138,7 @@ def is_usable(self): return False try: # Use a cursor directly, bypassing Django's utilities. - self.connection.cursor().execute('SELECT 1') + self.connection.cursor().execute("SELECT 1") except Database.Error: return False else: diff --git a/django_spanner/client.py b/google/cloud/spanner_django/client.py similarity index 83% rename from django_spanner/client.py rename to google/cloud/spanner_django/client.py index 1982437bb9..c45236d717 100644 --- a/django_spanner/client.py +++ b/google/cloud/spanner_django/client.py @@ -9,4 +9,4 @@ class DatabaseClient(BaseDatabaseClient): def runshell(self): - raise NotImplementedError('dbshell is not implemented.') + raise NotImplementedError("dbshell is not implemented.") diff --git a/django_spanner/compiler.py b/google/cloud/spanner_django/compiler.py similarity index 68% rename from django_spanner/compiler.py rename to google/cloud/spanner_django/compiler.py index eba4fc7c3e..713b65b054 100644 --- a/django_spanner/compiler.py +++ b/google/cloud/spanner_django/compiler.py @@ -7,7 +7,8 @@ from django.core.exceptions import EmptyResultSet from django.db.models.sql.compiler import ( SQLAggregateCompiler as BaseSQLAggregateCompiler, - SQLCompiler as BaseSQLCompiler, SQLDeleteCompiler as BaseSQLDeleteCompiler, + SQLCompiler as BaseSQLCompiler, + SQLDeleteCompiler as BaseSQLDeleteCompiler, SQLInsertCompiler as BaseSQLInsertCompiler, SQLUpdateCompiler as BaseSQLUpdateCompiler, ) @@ -24,14 +25,19 @@ def get_combinator_sql(self, combinator, all): features = self.connection.features compilers = [ query.get_compiler(self.using, self.connection) - for query in self.query.combined_queries if not query.is_empty() + for query in self.query.combined_queries + if not query.is_empty() ] if not features.supports_slicing_ordering_in_compound: for query, compiler in zip(self.query.combined_queries, compilers): if query.low_mark or query.high_mark: - raise DatabaseError('LIMIT/OFFSET not allowed in subqueries of compound statements.') + raise DatabaseError( + "LIMIT/OFFSET not allowed in subqueries of compound statements." + ) if compiler.get_order_by(): - raise DatabaseError('ORDER BY not allowed in subqueries of compound statements.') + raise DatabaseError( + "ORDER BY not allowed in subqueries of compound statements." + ) parts = () for compiler in compilers: try: @@ -39,35 +45,39 @@ def get_combinator_sql(self, combinator, all): # must have the same columns list. Set the selects defined on # the query on all combined queries, if not already set. if not compiler.query.values_select and self.query.values_select: - compiler.query.set_values(( - *self.query.extra_select, - *self.query.values_select, - *self.query.annotation_select, - )) + compiler.query.set_values( + ( + *self.query.extra_select, + *self.query.values_select, + *self.query.annotation_select, + ) + ) part_sql, part_args = compiler.as_sql() if compiler.query.combinator: # Wrap in a subquery if wrapping in parentheses isn't # supported. if not features.supports_parentheses_in_compound: - part_sql = 'SELECT * FROM ({})'.format(part_sql) + part_sql = "SELECT * FROM ({})".format(part_sql) # Add parentheses when combining with compound query if not # already added for all compound queries. elif not features.supports_slicing_ordering_in_compound: - part_sql = '({})'.format(part_sql) + part_sql = "({})".format(part_sql) parts += ((part_sql, part_args),) except EmptyResultSet: # Omit the empty queryset with UNION and with DIFFERENCE if the # first queryset is nonempty. - if combinator == 'union' or (combinator == 'difference' and parts): + if combinator == "union" or (combinator == "difference" and parts): continue raise if not parts: raise EmptyResultSet combinator_sql = self.connection.ops.set_operators[combinator] - combinator_sql += ' ALL' if all else ' DISTINCT' - braces = '({})' if features.supports_slicing_ordering_in_compound else '{}' - sql_parts, args_parts = zip(*((braces.format(sql), args) for sql, args in parts)) - result = [' {} '.format(combinator_sql).join(sql_parts)] + combinator_sql += " ALL" if all else " DISTINCT" + braces = "({})" if features.supports_slicing_ordering_in_compound else "{}" + sql_parts, args_parts = zip( + *((braces.format(sql), args) for sql, args in parts) + ) + result = [" {} ".format(combinator_sql).join(sql_parts)] params = [] for part in args_parts: params.extend(part) diff --git a/django_spanner/creation.py b/google/cloud/spanner_django/creation.py similarity index 72% rename from django_spanner/creation.py rename to google/cloud/spanner_django/creation.py index fe12f2f5a5..6776000380 100644 --- a/django_spanner/creation.py +++ b/google/cloud/spanner_django/creation.py @@ -17,18 +17,18 @@ class DatabaseCreation(BaseDatabaseCreation): def mark_skips(self): """Skip tests that don't work on Spanner.""" for test_name in self.connection.features.skip_tests: - test_case_name, _, method_name = test_name.rpartition('.') - test_app = test_name.split('.')[0] + test_case_name, _, method_name = test_name.rpartition(".") + test_app = test_name.split(".")[0] # Importing a test app that isn't installed raises RuntimeError. if test_app in settings.INSTALLED_APPS: test_case = import_string(test_case_name) method = getattr(test_case, method_name) - setattr(test_case, method_name, skip('unsupported by Spanner')(method)) + setattr(test_case, method_name, skip("unsupported by Spanner")(method)) def create_test_db(self, *args, **kwargs): # This environment variable is set by the Travis build script or # by a developer running the tests locally. - if os.environ.get('RUNNING_SPANNER_BACKEND_TESTS') == '1': + if os.environ.get("RUNNING_SPANNER_BACKEND_TESTS") == "1": self.mark_skips() super().create_test_db(*args, **kwargs) @@ -38,7 +38,7 @@ def _create_test_db(self, verbosity, autoclobber, keepdb=False): test_database_name = self._get_test_db_name() # Don't quote the test database name because google.cloud.spanner_v1 # does it. - test_db_params = {'dbname': test_database_name} + test_db_params = {"dbname": test_database_name} # Create the test database. try: self._execute_create_test_db(None, test_db_params, keepdb) @@ -47,29 +47,35 @@ def _create_test_db(self, verbosity, autoclobber, keepdb=False): # just return and skip it all. if keepdb: return test_database_name - self.log('Got an error creating the test database: %s' % e) + self.log("Got an error creating the test database: %s" % e) if not autoclobber: confirm = input( "Type 'yes' if you would like to try deleting the test " - "database '%s', or 'no' to cancel: " % test_database_name) - if autoclobber or confirm == 'yes': + "database '%s', or 'no' to cancel: " % test_database_name + ) + if autoclobber or confirm == "yes": try: if verbosity >= 1: - self.log('Destroying old test database for alias %s...' % ( - self._get_database_display_str(verbosity, test_database_name), - )) + self.log( + "Destroying old test database for alias %s..." + % ( + self._get_database_display_str( + verbosity, test_database_name + ), + ) + ) self._destroy_test_db(test_database_name, verbosity) self._execute_create_test_db(None, test_db_params, keepdb) except Exception as e: - self.log('Got an error recreating the test database: %s' % e) + self.log("Got an error recreating the test database: %s" % e) sys.exit(2) else: - self.log('Tests cancelled.') + self.log("Tests cancelled.") sys.exit(1) return test_database_name def _execute_create_test_db(self, cursor, parameters, keepdb=False): - self.connection.instance.database(parameters['dbname']).create() + self.connection.instance.database(parameters["dbname"]).create() def _destroy_test_db(self, test_database_name, verbosity): self.connection.instance.database(test_database_name).drop() diff --git a/django_spanner/expressions.py b/google/cloud/spanner_django/expressions.py similarity index 80% rename from django_spanner/expressions.py rename to google/cloud/spanner_django/expressions.py index 2e14702bf8..eaaad85abc 100644 --- a/django_spanner/expressions.py +++ b/google/cloud/spanner_django/expressions.py @@ -12,9 +12,9 @@ def order_by(self, compiler, connection, **extra_context): # DatabaseFeatures.supports_order_by_nulls_modifier = False. template = None if self.nulls_last: - template = '%(expression)s IS NULL, %(expression)s %(ordering)s' + template = "%(expression)s IS NULL, %(expression)s %(ordering)s" elif self.nulls_first: - template = '%(expression)s IS NOT NULL, %(expression)s %(ordering)s' + template = "%(expression)s IS NOT NULL, %(expression)s %(ordering)s" return self.as_sql(compiler, connection, template=template, **extra_context) diff --git a/google/cloud/spanner_django/features.py b/google/cloud/spanner_django/features.py new file mode 100644 index 0000000000..49704461e8 --- /dev/null +++ b/google/cloud/spanner_django/features.py @@ -0,0 +1,1774 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +import os + +from django.db.backends.base.features import BaseDatabaseFeatures +from django.db.utils import InterfaceError + + +class DatabaseFeatures(BaseDatabaseFeatures): + can_introspect_big_integer_field = False + can_introspect_duration_field = False + can_introspect_foreign_keys = False + # TimeField is introspected as DateTimeField because they both use + # TIMESTAMP. + can_introspect_time_field = False + closed_cursor_error_class = InterfaceError + # Spanner uses REGEXP_CONTAINS which is case-sensitive. + has_case_insensitive_like = False + # https://cloud.google.com/spanner/quotas#query_limits + max_query_params = 950 + supports_foreign_keys = False + supports_ignore_conflicts = False + supports_partial_indexes = False + supports_regex_backreferencing = False + supports_select_for_update_with_limit = False + supports_sequence_reset = False + supports_timezones = False + supports_transactions = False + supports_column_check_constraints = False + supports_table_check_constraints = False + uses_savepoints = False + + # Django tests that aren't supported by Spanner. + skip_tests = ( + # No foreign key constraints in Spanner. + "backends.tests.FkConstraintsTests.test_check_constraints", + "fixtures_regress.tests.TestFixtures.test_loaddata_raises_error_when_fixture_has_invalid_foreign_key", + # No Django transaction management in Spanner. + "basic.tests.SelectOnSaveTests.test_select_on_save_lying_update", + # spanner_django monkey patches AutoField to have a default value. + "basic.tests.ModelTest.test_hash", + "generic_relations.test_forms.GenericInlineFormsetTests.test_options", + "generic_relations.tests.GenericRelationsTests.test_unsaved_instance_on_generic_foreign_key", + "generic_relations_regress.tests.GenericRelationTests.test_target_model_is_unsaved", + "m2m_through_regress.tests.ToFieldThroughTests.test_m2m_relations_unusable_on_null_pk_obj", + "many_to_many.tests.ManyToManyTests.test_add", + "many_to_one.tests.ManyToOneTests.test_fk_assignment_and_related_object_cache", + "many_to_one.tests.ManyToOneTests.test_relation_unsaved", + "model_fields.test_durationfield.TestSerialization.test_dumping", + "model_fields.test_uuid.TestSerialization.test_dumping", + "model_fields.test_booleanfield.ValidationTest.test_nullbooleanfield_blank", + "model_inheritance.tests.ModelInheritanceTests.test_create_child_no_update", + "model_regress.tests.ModelTests.test_get_next_prev_by_field_unsaved", + "one_to_one.tests.OneToOneTests.test_get_reverse_on_unsaved_object", + "one_to_one.tests.OneToOneTests.test_set_reverse_on_unsaved_object", + "one_to_one.tests.OneToOneTests.test_unsaved_object", + "queries.test_bulk_update.BulkUpdateNoteTests.test_unsaved_models", + "serializers.test_json.JsonSerializerTestCase.test_pkless_serialized_strings", + "serializers.test_json.JsonSerializerTestCase.test_serialize_with_null_pk", + "serializers.test_xml.XmlSerializerTestCase.test_pkless_serialized_strings", + "serializers.test_xml.XmlSerializerTestCase.test_serialize_with_null_pk", + "serializers.test_yaml.YamlSerializerTestCase.test_pkless_serialized_strings", + "serializers.test_yaml.YamlSerializerTestCase.test_serialize_with_null_pk", + "timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime", + "timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_naive_datetime", + "validation.test_custom_messages.CustomMessagesTests.test_custom_null_message", + "validation.test_custom_messages.CustomMessagesTests.test_custom_simple_validator_message", + "validation.test_unique.PerformUniqueChecksTest.test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified", # noqa + "validation.test_unique.PerformUniqueChecksTest.test_primary_key_unique_check_not_performed_when_not_adding", + "validation.test_validators.TestModelsWithValidators.test_custom_validator_passes_for_correct_value", + "validation.test_validators.TestModelsWithValidators.test_custom_validator_raises_error_for_incorrect_value", + "validation.test_validators.TestModelsWithValidators.test_field_validators_can_be_any_iterable", + # Tests that assume a serial pk. + "admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter_nullbooleanfield", + "admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter_tuple", + "admin_filters.tests.ListFiltersTests.test_booleanfieldlistfilter", + "admin_filters.tests.ListFiltersTests.test_datefieldlistfilter_with_time_zone_support", + "admin_filters.tests.ListFiltersTests.test_datefieldlistfilter", + "admin_filters.tests.ListFiltersTests.test_fieldlistfilter_underscorelookup_tuple", + "admin_filters.tests.ListFiltersTests.test_fk_with_to_field", + "admin_filters.tests.ListFiltersTests.test_listfilter_genericrelation", + "admin_filters.tests.ListFiltersTests.test_lookup_with_non_string_value_underscored", + "admin_filters.tests.ListFiltersTests.test_lookup_with_non_string_value", + "admin_filters.tests.ListFiltersTests.test_relatedfieldlistfilter_manytomany", + "admin_filters.tests.ListFiltersTests.test_simplelistfilter", + "admin_inlines.tests.TestInline.test_inline_hidden_field_no_column", + "admin_utils.test_logentry.LogEntryTests.test_logentry_change_message", + "admin_utils.test_logentry.LogEntryTests.test_logentry_change_message_localized_datetime_input", + "admin_utils.test_logentry.LogEntryTests.test_proxy_model_content_type_is_used_for_log_entries", + "admin_views.tests.AdminViewPermissionsTest.test_history_view", + "aggregation.test_filter_argument.FilteredAggregateTests.test_plain_annotate", + "aggregation.tests.AggregateTestCase.test_annotate_basic", + "aggregation.tests.AggregateTestCase.test_annotation", + "aggregation.tests.AggregateTestCase.test_filtering", + "aggregation_regress.tests.AggregationTests.test_more_more", + "aggregation_regress.tests.AggregationTests.test_more_more_more", + "aggregation_regress.tests.AggregationTests.test_ticket_11293", + "defer_regress.tests.DeferRegressionTest.test_ticket_23270", + "distinct_on_fields.tests.DistinctOnTests.test_basic_distinct_on", + "extra_regress.tests.ExtraRegressTests.test_regression_7314_7372", + "generic_relations_regress.tests.GenericRelationTests.test_annotate", + "get_earliest_or_latest.tests.TestFirstLast", + "known_related_objects.tests.ExistingRelatedInstancesTests.test_reverse_one_to_one_multi_prefetch_related", + "known_related_objects.tests.ExistingRelatedInstancesTests.test_reverse_one_to_one_multi_select_related", + "lookup.tests.LookupTests.test_get_next_previous_by", + "lookup.tests.LookupTests.test_values_list", + "migrations.test_operations.OperationTests.test_alter_order_with_respect_to", + "model_fields.tests.GetChoicesOrderingTests.test_get_choices_reverse_related_field", + "model_formsets.tests.ModelFormsetTest.test_custom_pk", + "model_formsets_regress.tests.FormfieldShouldDeleteFormTests.test_custom_delete", + "multiple_database.tests.RouterTestCase.test_generic_key_cross_database_protection", + "ordering.tests.OrderingTests.test_default_ordering_by_f_expression", + "ordering.tests.OrderingTests.test_order_by_fk_attname", + "ordering.tests.OrderingTests.test_order_by_override", + "ordering.tests.OrderingTests.test_order_by_pk", + "prefetch_related.test_prefetch_related_objects.PrefetchRelatedObjectsTests.test_m2m_then_m2m", + "prefetch_related.tests.CustomPrefetchTests.test_custom_qs", + "prefetch_related.tests.CustomPrefetchTests.test_nested_prefetch_related_are_not_overwritten", + "prefetch_related.tests.DirectPrefechedObjectCacheReuseTests.test_detect_is_fetched", + "prefetch_related.tests.DirectPrefechedObjectCacheReuseTests.test_detect_is_fetched_with_to_attr", + "prefetch_related.tests.ForeignKeyToFieldTest.test_m2m", + "queries.test_bulk_update.BulkUpdateNoteTests.test_multiple_fields", + "queries.test_bulk_update.BulkUpdateTests.test_inherited_fields", + "queries.tests.Queries1Tests.test_ticket9411", + "queries.tests.Queries4Tests.test_ticket15316_exclude_true", + "queries.tests.Queries5Tests.test_ticket7256", + "queries.tests.SubqueryTests.test_related_sliced_subquery", + "queries.tests.Ticket14056Tests.test_ticket_14056", + "queries.tests.RelatedLookupTypeTests.test_values_queryset_lookup", + "raw_query.tests.RawQueryTests.test_annotations", + "raw_query.tests.RawQueryTests.test_get_item", + "select_related.tests.SelectRelatedTests.test_field_traversal", + "syndication_tests.tests.SyndicationFeedTest.test_rss2_feed", + "syndication_tests.tests.SyndicationFeedTest.test_latest_post_date", + "syndication_tests.tests.SyndicationFeedTest.test_rss091_feed", + "syndication_tests.tests.SyndicationFeedTest.test_template_feed", + # datetimes retrieved from the database with the wrong hour when + # USE_TZ = True: https://github.com/orijtech/spanner-orm/issues/193 + "datetimes.tests.DateTimesTests.test_21432", + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_func_with_timezone", + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_timezone_applied_before_truncation", # noqa + # extract() with timezone not working as expected: + # https://github.com/orijtech/spanner-orm/issues/191 + "timezones.tests.NewDatabaseTests.test_query_datetimes", + # using NULL with + crashes: https://github.com/orijtech/spanner-orm/issues/201 + "annotations.tests.NonAggregateAnnotationTestCase.test_combined_annotation_commutative", + # Spanner loses DecimalField precision due to conversion to float: + # https://github.com/orijtech/spanner-orm/pull/133#pullrequestreview-328482925 + "aggregation.tests.AggregateTestCase.test_decimal_max_digits_has_no_effect", + "aggregation.tests.AggregateTestCase.test_related_aggregate", + "db_functions.comparison.test_cast.CastTests.test_cast_to_decimal_field", + "model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding", + "model_fields.test_decimalfield.DecimalFieldTests.test_roundtrip_with_trailing_zeros", + # No CHECK constraints in Spanner. + "model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values", + # Spanner doesn't support the variance the standard deviation database + # functions: + "aggregation.test_filter_argument.FilteredAggregateTests.test_filtered_numerical_aggregates", + "aggregation_regress.tests.AggregationTests.test_stddev", + # SELECT list expression references which is neither grouped + # nor aggregated: https://github.com/orijtech/spanner-orm/issues/245 + "aggregation_regress.tests.AggregationTests.test_annotated_conditional_aggregate", + "aggregation_regress.tests.AggregationTests.test_annotation_with_value", + "expressions.tests.BasicExpressionsTests.test_filtering_on_annotate_that_uses_q", + # "No matching signature for operator" crash when comparing TIMESTAMP + # and DATE: https://github.com/orijtech/django-spanner/issues/255 + "expressions.tests.BasicExpressionsTests.test_outerref_mixed_case_table_name", + "expressions.tests.FTimeDeltaTests.test_mixed_comparisons1", + # duration arithmetic fails with dates: No matching signature for + # function TIMESTAMP_ADD: https://github.com/orijtech/django-spanner/issues/253 + "expressions.tests.FTimeDeltaTests.test_date_comparison", + "expressions.tests.FTimeDeltaTests.test_date_minus_duration", + "expressions.tests.FTimeDeltaTests.test_delta_add", + "expressions.tests.FTimeDeltaTests.test_duration_with_datetime", + "expressions.tests.FTimeDeltaTests.test_mixed_comparisons2", + # This test doesn't raise NotSupportedError because Spanner doesn't + # support select for update either (besides the "with limit" + # restriction). + "select_for_update.tests.SelectForUpdateTests.test_unsupported_select_for_update_with_limit", + # integer division produces a float result, which can't be assigned to + # an integer column: + # https://github.com/orijtech/django-spanner/issues/331 + "expressions.tests.ExpressionOperatorTests.test_lefthand_division", + "expressions.tests.ExpressionOperatorTests.test_right_hand_division", + # power operator produces a float result, which can't be assigned to + # an integer column: + # https://github.com/orijtech/django-spanner/issues/331 + "expressions.tests.ExpressionOperatorTests.test_lefthand_power", + "expressions.tests.ExpressionOperatorTests.test_righthand_power", + # Cloud Spanner's docs: "The rows that are returned by LIMIT and OFFSET + # is unspecified unless these operators are used after ORDER BY." + "aggregation_regress.tests.AggregationTests.test_sliced_conditional_aggregate", + "queries.tests.QuerySetBitwiseOperationTests.test_or_with_both_slice", + "queries.tests.QuerySetBitwiseOperationTests.test_or_with_both_slice_and_ordering", + "queries.tests.QuerySetBitwiseOperationTests.test_or_with_lhs_slice", + "queries.tests.QuerySetBitwiseOperationTests.test_or_with_rhs_slice", + "queries.tests.SubqueryTests.test_slice_subquery_and_query", + # Cloud Spanner limit: "Number of functions exceeds the maximum + # allowed limit of 1000." + "queries.test_bulk_update.BulkUpdateTests.test_large_batch", + # Spanner doesn't support random ordering. + "ordering.tests.OrderingTests.test_random_ordering", + # No matching signature for function MOD for argument types: FLOAT64, + # FLOAT64. Supported signatures: MOD(INT64, INT64) + "db_functions.math.test_mod.ModTests.test_decimal", + "db_functions.math.test_mod.ModTests.test_float", + # casting DateField to DateTimeField adds an unexpected hour: + # https://github.com/orijtech/spanner-orm/issues/260 + "db_functions.comparison.test_cast.CastTests.test_cast_from_db_date_to_datetime", + # Tests that fail during tear down on databases that don't support + # transactions: https://github.com/orijtech/spanner-orm/issues/271 + "admin_views.test_multidb.MultiDatabaseTests.test_add_view", + "admin_views.test_multidb.MultiDatabaseTests.test_change_view", + "admin_views.test_multidb.MultiDatabaseTests.test_delete_view", + "auth_tests.test_admin_multidb.MultiDatabaseTests.test_add_view", + "contenttypes_tests.test_models.ContentTypesMultidbTests.test_multidb", + # Tests that by-pass using spanner_django and generate + # invalid DDL: https://github.com/orijtech/django-spanner/issues/298 + "cache.tests.CreateCacheTableForDBCacheTests", + "cache.tests.DBCacheTests", + "cache.tests.DBCacheWithTimeZoneTests", + # Tests that require transactions. + "transaction_hooks.tests.TestConnectionOnCommit.test_does_not_execute_if_transaction_rolled_back", + "transaction_hooks.tests.TestConnectionOnCommit.test_hooks_cleared_after_rollback", + "transaction_hooks.tests.TestConnectionOnCommit.test_hooks_cleared_on_reconnect", + "transaction_hooks.tests.TestConnectionOnCommit.test_no_hooks_run_from_failed_transaction", + "transaction_hooks.tests.TestConnectionOnCommit.test_no_savepoints_atomic_merged_with_outer", + # Tests that require savepoints. + "get_or_create.tests.UpdateOrCreateTests.test_integrity", + "get_or_create.tests.UpdateOrCreateTests.test_manual_primary_key_test", + "get_or_create.tests.UpdateOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key", + "test_utils.tests.TestBadSetUpTestData.test_failure_in_setUpTestData_should_rollback_transaction", + "transaction_hooks.tests.TestConnectionOnCommit.test_discards_hooks_from_rolled_back_savepoint", + "transaction_hooks.tests.TestConnectionOnCommit.test_inner_savepoint_rolled_back_with_outer", + "transaction_hooks.tests.TestConnectionOnCommit.test_inner_savepoint_does_not_affect_outer", + # Spanner doesn't support views. + "inspectdb.tests.InspectDBTransactionalTests.test_include_views", + "introspection.tests.IntrospectionTests.test_table_names_with_views", + # No sequence for AutoField in Spanner. + "introspection.tests.IntrospectionTests.test_sequence_list", + # DatabaseIntrospection.get_key_columns() is only required if this + # backend needs it (which it currently doesn't). + "introspection.tests.IntrospectionTests.test_get_key_columns", + # DatabaseIntrospection.get_relations() isn't implemented: + # https://github.com/orijtech/django-spanner/issues/311 + "introspection.tests.IntrospectionTests.test_get_relations", + # pyformat parameters not supported on INSERT: + # https://github.com/orijtech/django-spanner/issues/343 + "backends.tests.BackendTestCase.test_cursor_execute_with_pyformat", + "backends.tests.BackendTestCase.test_cursor_executemany_with_pyformat", + "backends.tests.BackendTestCase.test_cursor_executemany_with_pyformat_iterator", + # duplicate table raises GoogleAPICallError rather than DatabaseError: + # https://github.com/orijtech/django-spanner/issues/344 + "backends.tests.BackendTestCase.test_duplicate_table_error", + "migrations.test_commands.MigrateTests.test_migrate_fake_initial", + "migrations.test_commands.MigrateTests.test_migrate_initial_false", + "migrations.test_executor.ExecutorTests.test_soft_apply", + # Spanner limitation: Cannot change type of column. + "migrations.test_executor.ExecutorTests.test_alter_id_type_with_fk", + "schema.tests.SchemaTests.test_alter_auto_field_to_char_field", + "schema.tests.SchemaTests.test_alter_text_field_to_date_field", + "schema.tests.SchemaTests.test_alter_text_field_to_datetime_field", + "schema.tests.SchemaTests.test_alter_text_field_to_time_field", + # Spanner limitation: Cannot rename tables and columns. + "contenttypes_tests.test_operations.ContentTypeOperationsTests", + "migrations.test_operations.OperationTests.test_alter_fk_non_fk", + "migrations.test_operations.OperationTests.test_alter_model_table", + "migrations.test_operations.OperationTests.test_alter_model_table_m2m", + "migrations.test_operations.OperationTests.test_rename_field", + "migrations.test_operations.OperationTests.test_rename_field_reloads_state_on_fk_target_changes", + "migrations.test_operations.OperationTests.test_rename_m2m_model_after_rename_field", + "migrations.test_operations.OperationTests.test_rename_m2m_target_model", + "migrations.test_operations.OperationTests.test_rename_m2m_through_model", + "migrations.test_operations.OperationTests.test_rename_model", + "migrations.test_operations.OperationTests.test_rename_model_with_m2m", + "migrations.test_operations.OperationTests.test_rename_model_with_self_referential_fk", + "migrations.test_operations.OperationTests.test_rename_model_with_self_referential_m2m", + "migrations.test_operations.OperationTests.test_rename_model_with_superclass_fk", + "migrations.test_operations.OperationTests.test_repoint_field_m2m", + "schema.tests.SchemaTests.test_alter_db_table_case", + "schema.tests.SchemaTests.test_alter_pk_with_self_referential_field", + "schema.tests.SchemaTests.test_rename", + "schema.tests.SchemaTests.test_db_table", + "schema.tests.SchemaTests.test_m2m_rename_field_in_target_model", + "schema.tests.SchemaTests.test_m2m_repoint", + "schema.tests.SchemaTests.test_m2m_repoint_custom", + "schema.tests.SchemaTests.test_m2m_repoint_inherited", + "schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references", + "schema.tests.SchemaTests.test_rename_keep_null_status", + "schema.tests.SchemaTests.test_rename_referenced_field", + "schema.tests.SchemaTests.test_rename_table_renames_deferred_sql_references", + "schema.tests.SchemaTests.test_referenced_field_without_constraint_rename_inside_atomic_block", + "schema.tests.SchemaTests.test_referenced_table_without_constraint_rename_inside_atomic_block", + "schema.tests.SchemaTests.test_unique_name_quoting", + # Spanner limitation: Cannot change a field to a primary key. + "schema.tests.SchemaTests.test_alter_not_unique_field_to_primary_key", + # Spanner limitation: Cannot drop column in primary key. + "schema.tests.SchemaTests.test_primary_key", + # Spanner limitation: Cannot remove a column from the primary key. + "schema.tests.SchemaTests.test_alter_int_pk_to_int_unique", + # Spanner limitation: migrations aren't atomic since Spanner doesn't + # support transactions. + "migrations.test_executor.ExecutorTests.test_atomic_operation_in_non_atomic_migration", + # changing a not null constraint isn't allowed if it affects an index: + # https://github.com/orijtech/django-spanner/issues/378 + "migrations.test_operations.OperationTests.test_alter_field_with_index", + # parsing INSERT with one inlined value and one placeholder fails: + # https://github.com/orijtech/django-spanner/issues/393 + "migrations.test_operations.OperationTests.test_run_sql_params", + # This test doesn't flush the database properly: + # https://code.djangoproject.com/ticket/31398 + "multiple_database.tests.AuthTestCase", + # This test isn't isolated on databases like Spanner that don't + # support transactions: https://code.djangoproject.com/ticket/31413 + "migrations.test_loader.LoaderTests.test_loading_squashed", + # Probably due to django-spanner setting a default on AutoField: + # https://github.com/googleapis/python-spanner-django/issues/422 + "model_inheritance_regress.tests.ModelInheritanceTest.test_issue_6755", + # Probably due to django-spanner setting a default on AutoField: + # https://github.com/googleapis/python-spanner-django/issues/424 + "model_formsets.tests.ModelFormsetTest.test_prevent_change_outer_model_and_create_invalid_data", + "model_formsets_regress.tests.FormfieldShouldDeleteFormTests.test_no_delete", + "model_formsets_regress.tests.FormsetTests.test_extraneous_query_is_not_run", + ) + # Kokoro-specific skips. + if os.environ.get("KOKORO_JOB_NAME"): + skip_tests += ( + # os.chmod() doesn't work on Kokoro? + "file_uploads.tests.DirectoryCreationTests.test_readonly_root", + # Tests that sometimes fail on Kokoro for unknown reasons. + "contenttypes_tests.test_models.ContentTypesTests.test_cache_not_shared_between_managers", + "migration_test_data_persistence.tests.MigrationDataNormalPersistenceTestCase.test_persistence", + "servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections", + ) + + if os.environ.get("SPANNER_EMULATOR_HOST", None): + # Some code isn't yet supported by the Spanner emulator. + skip_tests += ( + # Untyped parameters are not supported: + # https://github.com/GoogleCloudPlatform/cloud-spanner-emulator#features-and-limitations + "admin_changelist.test_date_hierarchy.DateHierarchyTests.test_bounded_params", # noqa + "admin_changelist.test_date_hierarchy.DateHierarchyTests.test_bounded_params_with_time_zone", # noqa + "admin_changelist.test_date_hierarchy.DateHierarchyTests.test_invalid_params", # noqa + "admin_changelist.tests.ChangeListTests.test_builtin_lookup_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_changelist_view_list_editable_changed_objects_uses_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_computed_list_display_localization", # noqa + "admin_changelist.tests.ChangeListTests.test_custom_lookup_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_custom_lookup_with_pk_shortcut", # noqa + "admin_changelist.tests.ChangeListTests.test_custom_paginator", # noqa + "admin_changelist.tests.ChangeListTests.test_deterministic_order_for_model_ordered_by_its_manager", # noqa + "admin_changelist.tests.ChangeListTests.test_deterministic_order_for_unordered_model", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_inherited_m2m_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_m2m_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_m2m_to_inherited_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_many_to_many_at_second_level_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_non_unique_related_object_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_non_unique_related_object_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_through_m2m_at_second_level_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_distinct_for_through_m2m_in_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_dynamic_list_display", # noqa + "admin_changelist.tests.ChangeListTests.test_dynamic_list_display_links", # noqa + "admin_changelist.tests.ChangeListTests.test_dynamic_list_filter", # noqa + "admin_changelist.tests.ChangeListTests.test_dynamic_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_get_edited_object_ids", # noqa + "admin_changelist.tests.ChangeListTests.test_get_list_editable_queryset", # noqa + "admin_changelist.tests.ChangeListTests.test_get_list_editable_queryset_with_regex_chars_in_prefix", # noqa + "admin_changelist.tests.ChangeListTests.test_get_select_related_custom_method", # noqa + "admin_changelist.tests.ChangeListTests.test_multiuser_edit", # noqa + "admin_changelist.tests.ChangeListTests.test_no_distinct_for_m2m_in_list_filter_without_params", # noqa + "admin_changelist.tests.ChangeListTests.test_no_list_display_links", # noqa + "admin_changelist.tests.ChangeListTests.test_object_tools_displayed_no_add_permission", # noqa + "admin_changelist.tests.ChangeListTests.test_pagination", # noqa + "admin_changelist.tests.ChangeListTests.test_pagination_page_range", # noqa + "admin_changelist.tests.ChangeListTests.test_pk_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_editable", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_editable_html", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_empty_changelist_value", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_html", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_set_empty_value_display_in_model_admin", # noqa + "admin_changelist.tests.ChangeListTests.test_result_list_set_empty_value_display_on_admin_site", # noqa + "admin_changelist.tests.ChangeListTests.test_select_related_as_empty_tuple", # noqa + "admin_changelist.tests.ChangeListTests.test_select_related_as_tuple", # noqa + "admin_changelist.tests.ChangeListTests.test_select_related_preserved", # noqa + "admin_changelist.tests.ChangeListTests.test_show_all", # noqa + "admin_changelist.tests.ChangeListTests.test_spanning_relations_with_custom_lookup_in_search_fields", # noqa + "admin_changelist.tests.ChangeListTests.test_specified_ordering_by_f_expression", # noqa + "admin_changelist.tests.ChangeListTests.test_specified_ordering_by_f_expression_without_asc_desc", # noqa + "admin_changelist.tests.ChangeListTests.test_total_ordering_optimization", # noqa + "admin_changelist.tests.ChangeListTests.test_tuple_list_display", # noqa + "admin_changelist.tests.GetAdminLogTests.test_no_user", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_add_with_GET_args", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_admin_URLs_no_clash", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_basic_add_GET", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_basic_add_POST", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_post_save_add_redirect", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_post_save_change_redirect", # noqa + "admin_custom_urls.tests.AdminCustomUrlsTest.test_post_url_continue", # noqa + "admin_docs.test_middleware.XViewMiddlewareTest.test_callable_object_view", # noqa + "admin_docs.test_middleware.XViewMiddlewareTest.test_xview_class", # noqa + "admin_docs.test_middleware.XViewMiddlewareTest.test_xview_func", # noqa + "admin_docs.test_views.AdminDocViewTests.test_bookmarklets", # noqa + "admin_docs.test_views.AdminDocViewTests.test_index", # noqa + "admin_docs.test_views.AdminDocViewTests.test_missing_docutils", # noqa + "admin_docs.test_views.AdminDocViewTests.test_model_index", # noqa + "admin_docs.test_views.AdminDocViewTests.test_namespaced_view_detail", # noqa + "admin_docs.test_views.AdminDocViewTests.test_no_sites_framework", # noqa + "admin_docs.test_views.AdminDocViewTests.test_template_detail", # noqa + "admin_docs.test_views.AdminDocViewTests.test_templatefilter_index", # noqa + "admin_docs.test_views.AdminDocViewTests.test_templatetag_index", # noqa + "admin_docs.test_views.AdminDocViewTests.test_view_detail", # noqa + "admin_docs.test_views.AdminDocViewTests.test_view_detail_as_method", # noqa + "admin_docs.test_views.AdminDocViewTests.test_view_detail_illegal_import", # noqa + "admin_docs.test_views.AdminDocViewTests.test_view_index", # noqa + "admin_docs.test_views.AdminDocViewTests.test_view_index_with_method", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_bookmarklets", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_index", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_missing_docutils", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_model_index", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_namespaced_view_detail", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_no_sites_framework", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_template_detail", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_templatefilter_index", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_templatetag_index", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail_as_method", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_detail_illegal_import", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_index", # noqa + "admin_docs.test_views.AdminDocViewWithMultipleEngines.test_view_index_with_method", # noqa + "admin_docs.test_views.TestModelDetailView.test_app_not_found", # noqa + "admin_docs.test_views.TestModelDetailView.test_descriptions_render_correctly", # noqa + "admin_docs.test_views.TestModelDetailView.test_instance_of_property_methods_are_displayed", # noqa + "admin_docs.test_views.TestModelDetailView.test_method_data_types", # noqa + "admin_docs.test_views.TestModelDetailView.test_method_excludes", # noqa + "admin_docs.test_views.TestModelDetailView.test_methods_with_arguments", # noqa + "admin_docs.test_views.TestModelDetailView.test_methods_with_arguments_display_arguments", # noqa + "admin_docs.test_views.TestModelDetailView.test_methods_with_arguments_display_arguments_default_value", # noqa + "admin_docs.test_views.TestModelDetailView.test_methods_with_multiple_arguments_display_arguments", # noqa + "admin_docs.test_views.TestModelDetailView.test_model_detail_title", # noqa + "admin_docs.test_views.TestModelDetailView.test_model_docstring_renders_correctly", # noqa + "admin_docs.test_views.TestModelDetailView.test_model_not_found", # noqa + "admin_docs.test_views.TestModelDetailView.test_model_with_many_to_one", # noqa + "admin_docs.test_views.TestModelDetailView.test_model_with_no_backward_relations_render_only_relevant_fields", # noqa + "admin_inlines.tests.TestInline.test_callable_lookup", # noqa + "admin_inlines.tests.TestInline.test_can_delete", # noqa + "admin_inlines.tests.TestInline.test_create_inlines_on_inherited_model", # noqa + "admin_inlines.tests.TestInline.test_custom_form_tabular_inline_label", # noqa + "admin_inlines.tests.TestInline.test_custom_form_tabular_inline_overridden_label", # noqa + "admin_inlines.tests.TestInline.test_custom_get_extra_form", # noqa + "admin_inlines.tests.TestInline.test_custom_min_num", # noqa + "admin_inlines.tests.TestInline.test_custom_pk_shortcut", # noqa + "admin_inlines.tests.TestInline.test_help_text", # noqa + "admin_inlines.tests.TestInline.test_inline_editable_pk", # noqa + "admin_inlines.tests.TestInline.test_inline_nonauto_noneditable_inherited_pk", # noqa + "admin_inlines.tests.TestInline.test_inline_nonauto_noneditable_pk", # noqa + "admin_inlines.tests.TestInline.test_inline_primary", # noqa + "admin_inlines.tests.TestInline.test_inlines_show_change_link_registered", # noqa + "admin_inlines.tests.TestInline.test_inlines_show_change_link_unregistered", # noqa + "admin_inlines.tests.TestInline.test_localize_pk_shortcut", # noqa + "admin_inlines.tests.TestInline.test_many_to_many_inlines", # noqa + "admin_inlines.tests.TestInline.test_min_num", # noqa + "admin_inlines.tests.TestInline.test_no_parent_callable_lookup", # noqa + "admin_inlines.tests.TestInline.test_non_related_name_inline", # noqa + "admin_inlines.tests.TestInline.test_noneditable_inline_has_field_inputs", # noqa + "admin_inlines.tests.TestInline.test_readonly_stacked_inline_label", # noqa + "admin_inlines.tests.TestInline.test_stacked_inline_edit_form_contains_has_original_class", # noqa + "admin_inlines.tests.TestInline.test_tabular_inline_column_css_class", # noqa + "admin_inlines.tests.TestInline.test_tabular_inline_show_change_link_false_registered", # noqa + "admin_inlines.tests.TestInline.test_tabular_model_form_meta_readonly_field", # noqa + "admin_inlines.tests.TestInline.test_tabular_non_field_errors", # noqa + "admin_inlines.tests.TestInlineMedia.test_all_inline_media", # noqa + "admin_inlines.tests.TestInlineMedia.test_inline_media_only_base", # noqa + "admin_inlines.tests.TestInlineMedia.test_inline_media_only_inline", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_add_fk_add_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_add_fk_noperm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_add_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_noperm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_add_m2m_view_only_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_add_change_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_add_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_all_perms", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_change_del_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_change_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_fk_noperm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_add_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_change_perm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_noperm", # noqa + "admin_inlines.tests.TestInlinePermissions.test_inline_change_m2m_view_only_perm", # noqa + "admin_inlines.tests.TestInlineProtectedOnDelete.test_deleting_inline_with_protected_delete_does_not_validate", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_add_url_not_allowed", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_extra_inlines_are_not_shown", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_get_to_change_url_is_allowed", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_inline_delete_buttons_are_not_shown", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_inlines_are_rendered_as_read_only", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_main_model_is_rendered_as_read_only", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_post_to_change_url_not_allowed", # noqa + "admin_inlines.tests.TestReadOnlyChangeViewInlinePermissions.test_submit_line_shows_only_close_button", # noqa + "admin_ordering.tests.TestAdminOrdering.test_dynamic_ordering", # noqa + "aggregation.tests.AggregateTestCase.test_add_implementation", # noqa + "aggregation.tests.AggregateTestCase.test_aggregate_alias", # noqa + "aggregation.tests.AggregateTestCase.test_aggregate_annotation", # noqa + "aggregation.tests.AggregateTestCase.test_aggregate_in_order_by", # noqa + "aggregation.tests.AggregateTestCase.test_aggregate_multi_join", # noqa + "aggregation.tests.AggregateTestCase.test_aggregate_over_complex_annotation", # noqa + "aggregation.tests.AggregateTestCase.test_aggregation_expressions", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_defer", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_defer_select_related", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_m2m", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_ordering", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_over_annotate", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_values", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_values_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_annotate_values_list", # noqa + "aggregation.tests.AggregateTestCase.test_annotated_aggregate_over_annotated_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_annotation_expressions", # noqa + "aggregation.tests.AggregateTestCase.test_arguments_must_be_expressions", # noqa + "aggregation.tests.AggregateTestCase.test_avg_decimal_field", # noqa + "aggregation.tests.AggregateTestCase.test_avg_duration_field", # noqa + "aggregation.tests.AggregateTestCase.test_backwards_m2m_annotate", # noqa + "aggregation.tests.AggregateTestCase.test_combine_different_types", # noqa + "aggregation.tests.AggregateTestCase.test_complex_aggregations_require_kwarg", # noqa + "aggregation.tests.AggregateTestCase.test_complex_values_aggregation", # noqa + "aggregation.tests.AggregateTestCase.test_count", # noqa + "aggregation.tests.AggregateTestCase.test_count_distinct_expression", # noqa + "aggregation.tests.AggregateTestCase.test_count_star", # noqa + "aggregation.tests.AggregateTestCase.test_dates_with_aggregation", # noqa + "aggregation.tests.AggregateTestCase.test_empty_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_even_more_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_expression_on_aggregation", # noqa + "aggregation.tests.AggregateTestCase.test_filter_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_fkey_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_grouped_annotation_in_group_by", # noqa + "aggregation.tests.AggregateTestCase.test_missing_output_field_raises_error", # noqa + "aggregation.tests.AggregateTestCase.test_more_aggregation", # noqa + "aggregation.tests.AggregateTestCase.test_multi_arg_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_multiple_aggregates", # noqa + "aggregation.tests.AggregateTestCase.test_non_grouped_annotation_not_in_group_by", # noqa + "aggregation.tests.AggregateTestCase.test_nonaggregate_aggregation_throws", # noqa + "aggregation.tests.AggregateTestCase.test_nonfield_annotation", # noqa + "aggregation.tests.AggregateTestCase.test_order_of_precedence", # noqa + "aggregation.tests.AggregateTestCase.test_reverse_fkey_annotate", # noqa + "aggregation.tests.AggregateTestCase.test_single_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_sum_distinct_aggregate", # noqa + "aggregation.tests.AggregateTestCase.test_sum_duration_field", # noqa + "aggregation.tests.AggregateTestCase.test_ticket11881", # noqa + "aggregation.tests.AggregateTestCase.test_ticket12886", # noqa + "aggregation.tests.AggregateTestCase.test_ticket17424", # noqa + "aggregation.tests.AggregateTestCase.test_values_aggregation", # noqa + "aggregation.tests.AggregateTestCase.test_values_annotation_with_expression", # noqa + "aggregation_regress.tests.JoinPromotionTests.test_ticket_21150", # noqa + "aggregation_regress.tests.SelfReferentialFKTests.test_ticket_24748", # noqa + "annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions", # noqa + "annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions_can_ref_other_functions", # noqa + "auth_tests.test_auth_backends.AllowAllUsersModelBackendTest.test_authenticate", # noqa + "auth_tests.test_auth_backends.AllowAllUsersModelBackendTest.test_get_user", # noqa + "auth_tests.test_auth_backends.AuthenticateTests.test_skips_backends_without_arguments", # noqa + "auth_tests.test_auth_backends.AuthenticateTests.test_type_error_raised", # noqa + "auth_tests.test_auth_backends.ChangedBackendSettingsTest.test_changed_backend_settings", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_anonymous_has_no_permissions", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_authentication_timing", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_custom_perms", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_get_all_superuser_permissions", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_has_no_object_perm", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.CustomPermissionsUserModelBackendTest.test_inactive_has_no_permissions", # noqa + "auth_tests.test_auth_backends.CustomUserModelBackendAuthenticateTest.test_authenticate", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_anonymous_has_no_permissions", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_authentication_timing", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_custom_perms", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_get_all_superuser_permissions", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_has_no_object_perm", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.ExtensionUserModelBackendTest.test_inactive_has_no_permissions", # noqa + "auth_tests.test_auth_backends.ImportedBackendTests.test_backend_path", # noqa + "auth_tests.test_auth_backends.ImproperlyConfiguredUserModelTest.test_does_not_shadow_exception", # noqa + "auth_tests.test_auth_backends.InActiveUserBackendTest.test_has_module_perms", # noqa + "auth_tests.test_auth_backends.InActiveUserBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_anonymous_has_no_permissions", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_authenticate_inactive", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_authenticate_user_without_is_active_field", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_authentication_timing", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_custom_perms", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_get_all_superuser_permissions", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_has_no_object_perm", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.ModelBackendTest.test_inactive_has_no_permissions", # noqa + "auth_tests.test_auth_backends.NoBackendsTest.test_raises_exception", # noqa + "auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_authenticates", # noqa + "auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_has_perm_denied", # noqa + "auth_tests.test_auth_backends.PermissionDeniedBackendTest.test_permission_denied", # noqa + "auth_tests.test_auth_backends.RowlevelBackendTest.test_get_all_permissions", # noqa + "auth_tests.test_auth_backends.RowlevelBackendTest.test_get_group_permissions", # noqa + "auth_tests.test_auth_backends.RowlevelBackendTest.test_has_perm", # noqa + "auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_with_explicit_backends", # noqa + "auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_without_authenticate_multiple_backends", # noqa + "auth_tests.test_auth_backends.SelectingBackendTests.test_backend_path_login_without_authenticate_single_backend", # noqa + "auth_tests.test_auth_backends.SelectingBackendTests.test_non_string_backend", # noqa + "auth_tests.test_auth_backends.UUIDUserTests.test_login", # noqa + "auth_tests.test_basic.BasicTestCase.test_superuser", # noqa + "auth_tests.test_basic.BasicTestCase.test_unicode_username", # noqa + "auth_tests.test_basic.BasicTestCase.test_user", # noqa + "auth_tests.test_basic.BasicTestCase.test_user_no_email", # noqa + "auth_tests.test_basic.TestGetUser.test_get_user", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_message_attrs", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_perm_in_perms_attrs", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_perms_attrs", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_session_is_accessed", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_session_not_accessed", # noqa + "auth_tests.test_context_processors.AuthContextProcessorTests.test_user_attrs", # noqa + "auth_tests.test_decorators.LoginRequiredTestCase.testCallable", # noqa + "auth_tests.test_decorators.LoginRequiredTestCase.testLoginRequired", # noqa + "auth_tests.test_decorators.LoginRequiredTestCase.testLoginRequiredNextUrl", # noqa + "auth_tests.test_decorators.LoginRequiredTestCase.testView", # noqa + "auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_many_permissions_in_set_pass", # noqa + "auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_many_permissions_pass", # noqa + "auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_permissioned_denied_exception_raised", # noqa + "auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_permissioned_denied_redirect", # noqa + "auth_tests.test_decorators.PermissionsRequiredDecoratorTest.test_single_permission_pass", # noqa + "auth_tests.test_forms.AdminPasswordChangeFormTest.test_missing_passwords", # noqa + "auth_tests.test_forms.AdminPasswordChangeFormTest.test_non_matching_passwords", # noqa + "auth_tests.test_forms.AdminPasswordChangeFormTest.test_one_password", # noqa + "auth_tests.test_forms.AdminPasswordChangeFormTest.test_password_whitespace_not_stripped", # noqa + "auth_tests.test_forms.AdminPasswordChangeFormTest.test_success", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_custom_login_allowed_policy", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_get_invalid_login_error", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_inactive_user", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_inactive_user_i18n", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_inactive_user_incorrect_password", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_integer_username", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_invalid_username", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_login_failed", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_password_whitespace_not_stripped", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_success", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_unicode_username", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_username_field_label", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_username_field_label_empty_string", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_username_field_label_not_set", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_username_field_max_length_defaults_to_254", # noqa + "auth_tests.test_forms.AuthenticationFormTest.test_username_field_max_length_matches_user_model", # noqa + "auth_tests.test_forms.PasswordChangeFormTest.test_field_order", # noqa + "auth_tests.test_forms.PasswordChangeFormTest.test_incorrect_password", # noqa + "auth_tests.test_forms.PasswordChangeFormTest.test_password_verification", # noqa + "auth_tests.test_forms.PasswordChangeFormTest.test_password_whitespace_not_stripped", # noqa + "auth_tests.test_forms.PasswordChangeFormTest.test_success", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_cleaned_data", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_custom_email_constructor", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_custom_email_field", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_custom_email_subject", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_inactive_user", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_invalid_email", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_nonexistent_email", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_preserve_username_case", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_save_html_email_template_name", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_save_plaintext_email", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_unusable_password", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_user_email_domain_unicode_collision", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_user_email_domain_unicode_collision_nonexistent", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_user_email_unicode_collision", # noqa + "auth_tests.test_forms.PasswordResetFormTest.test_user_email_unicode_collision_nonexistent", # noqa + "auth_tests.test_forms.SetPasswordFormTest.test_help_text_translation", # noqa + "auth_tests.test_forms.SetPasswordFormTest.test_password_verification", # noqa + "auth_tests.test_forms.SetPasswordFormTest.test_password_whitespace_not_stripped", # noqa + "auth_tests.test_forms.SetPasswordFormTest.test_success", # noqa + "auth_tests.test_forms.SetPasswordFormTest.test_validates_password", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_14242", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_17944_empty_password", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_17944_unknown_password_algorithm", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_17944_unmanageable_password", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_19133", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_bug_19349_bound_password_field", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_custom_form", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_password_excluded", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_unusable_password", # noqa + "auth_tests.test_forms.UserChangeFormTest.test_username_validity", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_both_passwords", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_custom_form", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_custom_form_hidden_username_field", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_custom_form_with_different_username_field", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_duplicate_normalized_unicode", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_invalid_data", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_normalize_username", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_password_help_text", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_password_verification", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_password_whitespace_not_stripped", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_success", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_unicode_username", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_user_already_exists", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_user_create_form_validates_password_with_all_data", # noqa + "auth_tests.test_forms.UserCreationFormTest.test_validates_password", # noqa + "auth_tests.test_handlers.ModWsgiHandlerTestCase.test_check_password", # noqa + "auth_tests.test_handlers.ModWsgiHandlerTestCase.test_check_password_custom_user", # noqa + "auth_tests.test_handlers.ModWsgiHandlerTestCase.test_groups_for_user", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_get_pass", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_get_pass_no_input", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_nonexistent_username", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_password_validation", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_system_username", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_changepassword_command_changes_joes_password", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_changepassword_command_works_with_nonascii_output", # noqa + "auth_tests.test_management.ChangepasswordManagementCommandTestCase.test_that_max_tries_exits_1", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_basic_usage", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_default_username", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_email_in_username", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username_non_interactive", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_existing_username_provided_via_option_and_interactive", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_fields_with_fk", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_fields_with_fk_interactive", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_invalid_username", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_non_ascii_verbose_name", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_passing_stdin", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_password_validation", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_password_validation_bypass", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_swappable_user", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_swappable_user_username_non_unique", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validate_password_against_required_fields", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validate_password_against_username", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validation_blank_password_entered", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_validation_mismatched_passwords", # noqa + "auth_tests.test_management.CreatesuperuserManagementCommandTestCase.test_verbosity_zero", # noqa + "auth_tests.test_management.GetDefaultUsernameTestCase.test_existing", # noqa + "auth_tests.test_management.MultiDBChangepasswordManagementCommandTestCase.test_that_changepassword_command_with_database_option_uses_given_db", # noqa + "auth_tests.test_management.MultiDBCreatesuperuserTestCase.test_createsuperuser_command_with_database_option", # noqa + "auth_tests.test_middleware.TestAuthenticationMiddleware.test_changed_password_invalidates_session", # noqa + "auth_tests.test_middleware.TestAuthenticationMiddleware.test_no_password_change_doesnt_invalidate_session", # noqa + "auth_tests.test_migrations.ProxyModelWithDifferentAppLabelTests.test_user_has_now_proxy_model_permissions", # noqa + "auth_tests.test_migrations.ProxyModelWithDifferentAppLabelTests.test_user_keeps_same_permissions_after_migrating_backward", # noqa + "auth_tests.test_migrations.ProxyModelWithSameAppLabelTests.test_user_keeps_same_permissions_after_migrating_backward", # noqa + "auth_tests.test_migrations.ProxyModelWithSameAppLabelTests.test_user_still_has_proxy_model_permissions", # noqa + "auth_tests.test_mixins.AccessMixinTests.test_access_mixin_permission_denied_response", # noqa + "auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_missing_permission", # noqa + "auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_not_logged_in", # noqa + "auth_tests.test_mixins.AccessMixinTests.test_stacked_mixins_success", # noqa + "auth_tests.test_mixins.LoginRequiredMixinTests.test_login_required", # noqa + "auth_tests.test_mixins.PermissionsRequiredMixinTests.test_many_permissions_pass", # noqa + "auth_tests.test_mixins.PermissionsRequiredMixinTests.test_permissioned_denied_exception_raised", # noqa + "auth_tests.test_mixins.PermissionsRequiredMixinTests.test_permissioned_denied_redirect", # noqa + "auth_tests.test_mixins.PermissionsRequiredMixinTests.test_single_permission_pass", # noqa + "auth_tests.test_models.AbstractUserTestCase.test_check_password_upgrade", # noqa + "auth_tests.test_models.AbstractUserTestCase.test_last_login_default", # noqa + "auth_tests.test_models.AbstractUserTestCase.test_user_double_save", # noqa + "auth_tests.test_models.IsActiveTestCase.test_builtin_user_isactive", # noqa + "auth_tests.test_models.IsActiveTestCase.test_is_active_field_default", # noqa + "auth_tests.test_models.NaturalKeysTestCase.test_user_natural_key", # noqa + "auth_tests.test_models.TestCreateSuperUserSignals.test_create_superuser", # noqa + "auth_tests.test_models.TestCreateSuperUserSignals.test_create_user", # noqa + "auth_tests.test_models.UserManagerTestCase.test_create_user", # noqa + "auth_tests.test_models.UserManagerTestCase.test_create_user_is_staff", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_known_user", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_last_login", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_unknown_user", # noqa + "auth_tests.test_remote_user.AllowAllUsersRemoteUserBackendTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_known_user", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_last_login", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_unknown_user", # noqa + "auth_tests.test_remote_user.CustomHeaderRemoteUserTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_known_user", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_last_login", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_unknown_user", # noqa + "auth_tests.test_remote_user.PersistentRemoteUserTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_known_user", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_last_login", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_unknown_user", # noqa + "auth_tests.test_remote_user.RemoteUserCustomTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user.RemoteUserNoCreateTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.RemoteUserNoCreateTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.RemoteUserNoCreateTest.test_known_user", # noqa + "auth_tests.test_remote_user.RemoteUserNoCreateTest.test_last_login", # noqa + "auth_tests.test_remote_user.RemoteUserNoCreateTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_header_disappears", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_inactive_user", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_known_user", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_last_login", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_unknown_user", # noqa + "auth_tests.test_remote_user.RemoteUserTest.test_user_switch_forces_new_login", # noqa + "auth_tests.test_remote_user_deprecation.RemoteUserCustomTest.test_configure_user_deprecation_warning", # noqa + "auth_tests.test_signals.SignalTestCase.test_failed_login_without_request", # noqa + "auth_tests.test_signals.SignalTestCase.test_login", # noqa + "auth_tests.test_signals.SignalTestCase.test_login_with_custom_user_without_last_login_field", # noqa + "auth_tests.test_signals.SignalTestCase.test_logout", # noqa + "auth_tests.test_signals.SignalTestCase.test_logout_anonymous", # noqa + "auth_tests.test_signals.SignalTestCase.test_update_last_login", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordChangeDoneView", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetChangeView", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetCompleteView", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetConfirmView_invalid_token", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetConfirmView_valid_token", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetDoneView", # noqa + "auth_tests.test_templates.AuthTemplateTests.test_PasswordResetView", # noqa + "auth_tests.test_tokens.TokenGeneratorTest.test_10265", # noqa + "auth_tests.test_tokens.TokenGeneratorTest.test_check_token_with_nonexistent_token_and_user", # noqa + "auth_tests.test_tokens.TokenGeneratorTest.test_make_token", # noqa + "auth_tests.test_tokens.TokenGeneratorTest.test_timeout", # noqa + "auth_tests.test_tokens.TokenGeneratorTest.test_token_with_different_secret", # noqa + "auth_tests.test_validators.UserAttributeSimilarityValidatorTest.test_validate", # noqa + "auth_tests.test_views.AuthViewNamedURLTests.test_named_urls", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_done_fails", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_done_succeeds", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_fails_with_invalid_old_password", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_fails_with_mismatched_passwords", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_custom", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_custom_named", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_redirect_default", # noqa + "auth_tests.test_views.ChangePasswordTest.test_password_change_succeeds", # noqa + "auth_tests.test_views.ChangelistTests.test_changelist_disallows_password_lookups", # noqa + "auth_tests.test_views.ChangelistTests.test_password_change_bad_url", # noqa + "auth_tests.test_views.ChangelistTests.test_user_change_different_user_password", # noqa + "auth_tests.test_views.ChangelistTests.test_user_change_email", # noqa + "auth_tests.test_views.ChangelistTests.test_user_change_password", # noqa + "auth_tests.test_views.ChangelistTests.test_user_change_password_passes_user_to_has_change_permission", # noqa + "auth_tests.test_views.ChangelistTests.test_user_not_change", # noqa + "auth_tests.test_views.ChangelistTests.test_view_user_password_is_readonly", # noqa + "auth_tests.test_views.CustomUserPasswordResetTest.test_confirm_valid_custom_user", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_default", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_guest", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_permission_required_logged_in", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_permission_required_not_logged_in", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_loop", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_param", # noqa + "auth_tests.test_views.LoginRedirectAuthenticatedUser.test_redirect_url", # noqa + "auth_tests.test_views.LoginRedirectUrlTest.test_custom", # noqa + "auth_tests.test_views.LoginRedirectUrlTest.test_default", # noqa + "auth_tests.test_views.LoginRedirectUrlTest.test_named", # noqa + "auth_tests.test_views.LoginRedirectUrlTest.test_remote", # noqa + "auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_safe_host", # noqa + "auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_same_host", # noqa + "auth_tests.test_views.LoginSuccessURLAllowedHostsTest.test_success_url_allowed_hosts_unsafe_host", # noqa + "auth_tests.test_views.LoginTest.test_current_site_in_context_after_login", # noqa + "auth_tests.test_views.LoginTest.test_login_csrf_rotate", # noqa + "auth_tests.test_views.LoginTest.test_login_form_contains_request", # noqa + "auth_tests.test_views.LoginTest.test_login_session_without_hash_session_key", # noqa + "auth_tests.test_views.LoginTest.test_security_check", # noqa + "auth_tests.test_views.LoginTest.test_security_check_https", # noqa + "auth_tests.test_views.LoginTest.test_session_key_flushed_on_login", # noqa + "auth_tests.test_views.LoginTest.test_session_key_flushed_on_login_after_password_change", # noqa + "auth_tests.test_views.LoginURLSettings.test_https_login_url", # noqa + "auth_tests.test_views.LoginURLSettings.test_lazy_login_url", # noqa + "auth_tests.test_views.LoginURLSettings.test_login_url_with_querystring", # noqa + "auth_tests.test_views.LoginURLSettings.test_named_login_url", # noqa + "auth_tests.test_views.LoginURLSettings.test_remote_login_url", # noqa + "auth_tests.test_views.LoginURLSettings.test_remote_login_url_with_next_querystring", # noqa + "auth_tests.test_views.LoginURLSettings.test_standard_login_url", # noqa + "auth_tests.test_views.LogoutTest.test_14377", # noqa + "auth_tests.test_views.LogoutTest.test_logout_default", # noqa + "auth_tests.test_views.LogoutTest.test_logout_doesnt_cache", # noqa + "auth_tests.test_views.LogoutTest.test_logout_preserve_language", # noqa + "auth_tests.test_views.LogoutTest.test_logout_redirect_url_named_setting", # noqa + "auth_tests.test_views.LogoutTest.test_logout_redirect_url_setting", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_custom_redirect_argument", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_named_redirect", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_next_page_specified", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_overridden_redirect_url", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_post", # noqa + "auth_tests.test_views.LogoutTest.test_logout_with_redirect_argument", # noqa + "auth_tests.test_views.LogoutTest.test_security_check", # noqa + "auth_tests.test_views.LogoutTest.test_security_check_https", # noqa + "auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_safe_host", # noqa + "auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_same_host", # noqa + "auth_tests.test_views.LogoutTest.test_success_url_allowed_hosts_unsafe_host", # noqa + "auth_tests.test_views.LogoutThenLoginTests.test_default_logout_then_login", # noqa + "auth_tests.test_views.LogoutThenLoginTests.test_logout_then_login_with_custom_login", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_complete", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_different_passwords", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_display_user_from_form", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_invalid", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_invalid_hash", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_invalid_post", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_invalid_user", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_link_redirects_to_set_password_page", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset_already_logged_in", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_login_post_reset_custom_backend", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_overflow_user", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_redirect_custom", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_redirect_custom_named", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_redirect_default", # noqa + "auth_tests.test_views.PasswordResetTest.test_confirm_valid", # noqa + "auth_tests.test_views.PasswordResetTest.test_email_found", # noqa + "auth_tests.test_views.PasswordResetTest.test_email_found_custom_from", # noqa + "auth_tests.test_views.PasswordResetTest.test_email_not_found", # noqa + "auth_tests.test_views.PasswordResetTest.test_extra_email_context", # noqa + "auth_tests.test_views.PasswordResetTest.test_html_mail_template", # noqa + "auth_tests.test_views.PasswordResetTest.test_invalid_link_if_going_directly_to_the_final_reset_password_url", # noqa + "auth_tests.test_views.PasswordResetTest.test_poisoned_http_host", # noqa + "auth_tests.test_views.PasswordResetTest.test_poisoned_http_host_admin_site", # noqa + "auth_tests.test_views.PasswordResetTest.test_reset_custom_redirect", # noqa + "auth_tests.test_views.PasswordResetTest.test_reset_custom_redirect_named", # noqa + "auth_tests.test_views.PasswordResetTest.test_reset_redirect_default", # noqa + "auth_tests.test_views.RedirectToLoginTests.test_redirect_to_login_with_lazy", # noqa + "auth_tests.test_views.RedirectToLoginTests.test_redirect_to_login_with_lazy_and_unicode", # noqa + "auth_tests.test_views.SessionAuthenticationTests.test_user_password_change_updates_session", # noqa + "auth_tests.test_views.UUIDUserPasswordResetTest.test_confirm_invalid_uuid", # noqa + "auth_tests.test_views.UUIDUserPasswordResetTest.test_confirm_valid_custom_user", # noqa + "auth_tests.test_views.UUIDUserTests.test_admin_password_change", # noqa + "backends.tests.FkConstraintsTests.test_disable_constraint_checks_context_manager", # noqa + "backends.tests.FkConstraintsTests.test_disable_constraint_checks_manually", # noqa + "backends.tests.FkConstraintsTests.test_integrity_checks_on_creation", # noqa + "backends.tests.FkConstraintsTests.test_integrity_checks_on_update", # noqa + "basic.tests.ModelTest.test_ticket_20278", + "basic.tests.ModelRefreshTests.test_lookup_in_fields", + "basic.tests.ModelRefreshTests.test_prefetched_cache_cleared", + "basic.tests.ModelRefreshTests.test_lookup_in_fields", + "basic.tests.ModelRefreshTests.test_prefetched_cache_cleared", + "basic.tests.ModelRefreshTests.test_refresh_fk", + "basic.tests.ModelRefreshTests.test_refresh_fk_on_delete_set_null", + "basic.tests.ModelRefreshTests.test_refresh_null_fk", + "basic.tests.ModelRefreshTests.test_unknown_kwarg", + "bulk_create.tests.BulkCreateTests.test_bulk_insert_nullable_fields", # noqa + "custom_pk.tests.CustomPKTests.test_required_pk", # noqa + "custom_pk.tests.CustomPKTests.test_unique_pk", # noqa + "datatypes.tests.DataTypesTestCase.test_boolean_type", # noqa + "datatypes.tests.DataTypesTestCase.test_date_type", # noqa + "datatypes.tests.DataTypesTestCase.test_textfields_str", # noqa + "datatypes.tests.DataTypesTestCase.test_time_field", # noqa + "datatypes.tests.DataTypesTestCase.test_year_boundaries", # noqa + "dates.tests.DatesTests.test_related_model_traverse", # noqa + "datetimes.tests.DateTimesTests.test_datetimes_has_lazy_iterator", # noqa + "datetimes.tests.DateTimesTests.test_datetimes_returns_available_dates_for_given_scope_and_given_field", # noqa + "datetimes.tests.DateTimesTests.test_related_model_traverse", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_date", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_date_group_by", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_db_datetime_to_time", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_field", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_python", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_date", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_python_to_datetime", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_from_value", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_to_char_field_with_max_length", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_to_char_field_without_max_length", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_to_integer", # noqa + "db_functions.comparison.test_cast.CastTests.test_cast_to_text_field", # noqa + "db_functions.comparison.test_coalesce.CoalesceTests.test_basic", # noqa + "db_functions.comparison.test_coalesce.CoalesceTests.test_mixed_values", # noqa + "db_functions.comparison.test_coalesce.CoalesceTests.test_ordering", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_all_null", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_basic", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_coalesce_workaround", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_propagates_null", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_related_field", # noqa + "db_functions.comparison.test_greatest.GreatestTests.test_update", # noqa + "db_functions.comparison.test_least.LeastTests.test_all_null", # noqa + "db_functions.comparison.test_least.LeastTests.test_basic", # noqa + "db_functions.comparison.test_least.LeastTests.test_coalesce_workaround", # noqa + "db_functions.comparison.test_least.LeastTests.test_propagates_null", # noqa + "db_functions.comparison.test_least.LeastTests.test_related_field", # noqa + "db_functions.comparison.test_least.LeastTests.test_update", # noqa + "db_functions.comparison.test_nullif.NullIfTests.test_basic", # noqa + "db_functions.comparison.test_nullif.NullIfTests.test_null_argument", # noqa + "db_functions.comparison.test_nullif.NullIfTests.test_too_few_args", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_date_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_subquery_with_parameters", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_time_func", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionTests.test_trunc_time_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_extract_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_date_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_none", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_subquery_with_parameters", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_time_func", # noqa + "db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_trunc_time_none", # noqa + "db_functions.datetime.test_now.NowTests.test_basic", # noqa + "db_functions.math.test_abs.AbsTests.test_null", # noqa + "db_functions.math.test_acos.ACosTests.test_null", # noqa + "db_functions.math.test_asin.ASinTests.test_null", # noqa + "db_functions.math.test_atan.ATanTests.test_null", # noqa + "db_functions.math.test_atan2.ATan2Tests.test_null", # noqa + "db_functions.math.test_ceil.CeilTests.test_decimal", # noqa + "db_functions.math.test_ceil.CeilTests.test_float", # noqa + "db_functions.math.test_ceil.CeilTests.test_integer", # noqa + "db_functions.math.test_ceil.CeilTests.test_null", # noqa + "db_functions.math.test_ceil.CeilTests.test_transform", # noqa + "db_functions.math.test_cos.CosTests.test_null", # noqa + "db_functions.math.test_cot.CotTests.test_null", # noqa + "db_functions.math.test_degrees.DegreesTests.test_null", # noqa + "db_functions.math.test_exp.ExpTests.test_null", # noqa + "db_functions.math.test_floor.FloorTests.test_null", # noqa + "db_functions.math.test_ln.LnTests.test_null", # noqa + "db_functions.math.test_log.LogTests.test_null", # noqa + "db_functions.math.test_mod.ModTests.test_null", # noqa + "db_functions.math.test_power.PowerTests.test_decimal", # noqa + "db_functions.math.test_power.PowerTests.test_float", # noqa + "db_functions.math.test_power.PowerTests.test_integer", # noqa + "db_functions.math.test_power.PowerTests.test_null", # noqa + "db_functions.math.test_radians.RadiansTests.test_null", # noqa + "db_functions.math.test_round.RoundTests.test_null", # noqa + "db_functions.math.test_sin.SinTests.test_null", # noqa + "db_functions.math.test_sqrt.SqrtTests.test_null", # noqa + "db_functions.math.test_tan.TanTests.test_null", # noqa + "db_functions.tests.FunctionTests.test_func_transform_bilateral", # noqa + "db_functions.tests.FunctionTests.test_func_transform_bilateral_multivalue", # noqa + "db_functions.tests.FunctionTests.test_function_as_filter", # noqa + "db_functions.tests.FunctionTests.test_nested_function_ordering", # noqa + "db_functions.text.test_chr.ChrTests.test_basic", # noqa + "db_functions.text.test_chr.ChrTests.test_non_ascii", # noqa + "db_functions.text.test_chr.ChrTests.test_transform", # noqa + "db_functions.text.test_concat.ConcatTests.test_basic", # noqa + "db_functions.text.test_concat.ConcatTests.test_many", # noqa + "db_functions.text.test_concat.ConcatTests.test_mixed_char_text", # noqa + "db_functions.text.test_left.LeftTests.test_basic", # noqa + "db_functions.text.test_left.LeftTests.test_expressions", # noqa + "db_functions.text.test_left.LeftTests.test_invalid_length", # noqa + "db_functions.text.test_length.LengthTests.test_basic", # noqa + "db_functions.text.test_length.LengthTests.test_ordering", # noqa + "db_functions.text.test_length.LengthTests.test_transform", # noqa + "db_functions.text.test_lower.LowerTests.test_basic", # noqa + "db_functions.text.test_lower.LowerTests.test_transform", # noqa + "db_functions.text.test_ord.OrdTests.test_basic", # noqa + "db_functions.text.test_ord.OrdTests.test_transform", # noqa + "db_functions.text.test_pad.PadTests.test_combined_with_length", # noqa + "db_functions.text.test_pad.PadTests.test_pad", # noqa + "db_functions.text.test_repeat.RepeatTests.test_basic", # noqa + "db_functions.text.test_replace.ReplaceTests.test_case_sensitive", # noqa + "db_functions.text.test_replace.ReplaceTests.test_replace_expression", # noqa + "db_functions.text.test_replace.ReplaceTests.test_replace_with_default_arg", # noqa + "db_functions.text.test_replace.ReplaceTests.test_replace_with_empty_string", # noqa + "db_functions.text.test_replace.ReplaceTests.test_update", # noqa + "db_functions.text.test_reverse.ReverseTests.test_basic", # noqa + "db_functions.text.test_reverse.ReverseTests.test_expressions", # noqa + "db_functions.text.test_reverse.ReverseTests.test_null", # noqa + "db_functions.text.test_reverse.ReverseTests.test_transform", # noqa + "db_functions.text.test_right.RightTests.test_basic", # noqa + "db_functions.text.test_right.RightTests.test_expressions", # noqa + "db_functions.text.test_right.RightTests.test_invalid_length", # noqa + "db_functions.text.test_strindex.StrIndexTests.test_annotate_charfield", # noqa + "db_functions.text.test_strindex.StrIndexTests.test_annotate_textfield", # noqa + "db_functions.text.test_strindex.StrIndexTests.test_filtering", # noqa + "db_functions.text.test_strindex.StrIndexTests.test_order_by", # noqa + "db_functions.text.test_strindex.StrIndexTests.test_unicode_values", # noqa + "db_functions.text.test_substr.SubstrTests.test_basic", # noqa + "db_functions.text.test_substr.SubstrTests.test_expressions", # noqa + "db_functions.text.test_substr.SubstrTests.test_start", # noqa + "db_functions.text.test_trim.TrimTests.test_trim", # noqa + "db_functions.text.test_trim.TrimTests.test_trim_transform", # noqa + "db_functions.text.test_upper.UpperTests.test_basic", # noqa + "db_functions.text.test_upper.UpperTests.test_transform", # noqa + "defer_regress.tests.DeferAnnotateSelectRelatedTest.test_defer_annotate_select_related", # noqa + "delete_regress.tests.DeleteCascadeTransactionTests.test_inheritance", # noqa + "expressions.test_queryset_values.ValuesExpressionsTests.test_chained_values_with_expression", # noqa + "expressions.test_queryset_values.ValuesExpressionsTests.test_values_expression", # noqa + "expressions.test_queryset_values.ValuesExpressionsTests.test_values_expression_group_by", # noqa + "expressions.test_queryset_values.ValuesExpressionsTests.test_values_list_expression", # noqa + "expressions.test_queryset_values.ValuesExpressionsTests.test_values_list_expression_flat", # noqa + "expressions.tests.BasicExpressionsTests.test_annotate_values_aggregate", # noqa + "expressions.tests.BasicExpressionsTests.test_annotate_values_filter", # noqa + "expressions.tests.BasicExpressionsTests.test_annotations_within_subquery", # noqa + "expressions.tests.BasicExpressionsTests.test_arithmetic", # noqa + "expressions.tests.BasicExpressionsTests.test_exist_single_field_output_field", # noqa + "expressions.tests.BasicExpressionsTests.test_explicit_output_field", # noqa + "expressions.tests.BasicExpressionsTests.test_filter_inter_attribute", # noqa + "expressions.tests.BasicExpressionsTests.test_filter_with_join", # noqa + "expressions.tests.BasicExpressionsTests.test_in_subquery", # noqa + "expressions.tests.BasicExpressionsTests.test_incorrect_field_in_F_expression", # noqa + "expressions.tests.BasicExpressionsTests.test_incorrect_joined_field_in_F_expression", # noqa + "expressions.tests.BasicExpressionsTests.test_nested_subquery", # noqa + "expressions.tests.BasicExpressionsTests.test_nested_subquery_outer_ref_2", # noqa + "expressions.tests.BasicExpressionsTests.test_nested_subquery_outer_ref_with_autofield", # noqa + "expressions.tests.BasicExpressionsTests.test_new_object_create", # noqa + "expressions.tests.BasicExpressionsTests.test_new_object_save", # noqa + "expressions.tests.BasicExpressionsTests.test_object_create_with_aggregate", # noqa + "expressions.tests.BasicExpressionsTests.test_object_update", # noqa + "expressions.tests.BasicExpressionsTests.test_object_update_fk", # noqa + "expressions.tests.BasicExpressionsTests.test_object_update_unsaved_objects", # noqa + "expressions.tests.BasicExpressionsTests.test_order_by_exists", # noqa + "expressions.tests.BasicExpressionsTests.test_order_of_operations", # noqa + "expressions.tests.BasicExpressionsTests.test_outerref", # noqa + "expressions.tests.BasicExpressionsTests.test_outerref_with_operator", # noqa + "expressions.tests.BasicExpressionsTests.test_parenthesis_priority", # noqa + "expressions.tests.BasicExpressionsTests.test_pickle_expression", # noqa + "expressions.tests.BasicExpressionsTests.test_subquery", # noqa + "expressions.tests.BasicExpressionsTests.test_subquery_filter_by_aggregate", # noqa + "expressions.tests.BasicExpressionsTests.test_subquery_references_joined_table_twice", # noqa + "expressions.tests.BasicExpressionsTests.test_ticket_11722_iexact_lookup", # noqa + "expressions.tests.BasicExpressionsTests.test_ticket_18375_chained_filters", # noqa + "expressions.tests.BasicExpressionsTests.test_ticket_18375_join_reuse", # noqa + "expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering", # noqa + "expressions.tests.BasicExpressionsTests.test_ticket_18375_kwarg_ordering_2", # noqa + "expressions.tests.BasicExpressionsTests.test_update", # noqa + "expressions.tests.BasicExpressionsTests.test_update_inherited_field_value", # noqa + "expressions.tests.BasicExpressionsTests.test_update_with_fk", # noqa + "expressions.tests.BasicExpressionsTests.test_update_with_none", # noqa + "expressions.tests.BasicExpressionsTests.test_uuid_pk_subquery", # noqa + "expressions.tests.ExpressionsNumericTests.test_complex_expressions", # noqa + "expressions.tests.ExpressionsNumericTests.test_fill_with_value_from_same_object", # noqa + "expressions.tests.ExpressionsNumericTests.test_filter_not_equals_other_field", # noqa + "expressions.tests.ExpressionsNumericTests.test_increment_value", # noqa + "expressions.tests.ExpressionsTests.test_F_reuse", # noqa + "expressions.tests.IterableLookupInnerExpressionsTests.test_expressions_in_lookups_join_choice", # noqa + "expressions.tests.IterableLookupInnerExpressionsTests.test_in_lookup_allows_F_expressions_and_expressions_for_datetimes", # noqa + "expressions.tests.IterableLookupInnerExpressionsTests.test_in_lookup_allows_F_expressions_and_expressions_for_integers", # noqa + "expressions.tests.IterableLookupInnerExpressionsTests.test_range_lookup_allows_F_expressions_and_expressions_for_integers", # noqa + "expressions.tests.ValueTests.test_update_TimeField_using_Value", # noqa + "expressions.tests.ValueTests.test_update_UUIDField_using_Value", # noqa + "fixtures.tests.FixtureLoadingTests.test_loaddata_error_message", # noqa + "fixtures.tests.ForwardReferenceTests.test_forward_reference_fk", # noqa + "fixtures.tests.ForwardReferenceTests.test_forward_reference_m2m", # noqa + "get_or_create.tests.GetOrCreateTests.test_get_or_create_invalid_params", # noqa + "get_or_create.tests.GetOrCreateTestsWithManualPKs.test_create_with_duplicate_primary_key", # noqa + "get_or_create.tests.GetOrCreateTestsWithManualPKs.test_get_or_create_raises_IntegrityError_plus_traceback", # noqa + "introspection.tests.IntrospectionTests.test_get_constraints", # noqa + "introspection.tests.IntrospectionTests.test_get_constraints_index_types", # noqa + "introspection.tests.IntrospectionTests.test_get_constraints_indexes_orders", # noqa + "introspection.tests.IntrospectionTests.test_get_primary_key_column", # noqa + "lookup.tests.LookupTests.test_custom_field_none_rhs", # noqa + "lookup.tests.LookupTests.test_custom_lookup_none_rhs", # noqa + "lookup.tests.LookupTests.test_escaping", # noqa + "lookup.tests.LookupTests.test_exact_none_transform", # noqa + "lookup.tests.LookupTests.test_exclude", # noqa + "lookup.tests.LookupTests.test_in_bulk_lots_of_ids", # noqa + "lookup.tests.LookupTests.test_lookup_collision", # noqa + "lookup.tests.LookupTests.test_regex", # noqa + "lookup.tests.LookupTests.test_regex_non_string", # noqa + "lookup.tests.LookupTests.test_regex_null", # noqa + "m2m_through.tests.M2mThroughReferentialTests.test_through_fields_self_referential", # noqa + "m2m_through.tests.M2mThroughTests.test_add_on_m2m_with_intermediate_model_value_required_fails", # noqa + "m2m_through.tests.M2mThroughTests.test_add_on_reverse_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_clear_on_reverse_removes_all_the_m2m_relationships", # noqa + "m2m_through.tests.M2mThroughTests.test_clear_removes_all_the_m2m_relationships", # noqa + "m2m_through.tests.M2mThroughTests.test_create_on_m2m_with_intermediate_model_value_required_fails", # noqa + "m2m_through.tests.M2mThroughTests.test_create_on_reverse_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_custom_related_name_doesnt_conflict_with_fky_related_name", # noqa + "m2m_through.tests.M2mThroughTests.test_custom_related_name_forward_non_empty_qs", # noqa + "m2m_through.tests.M2mThroughTests.test_custom_related_name_reverse_non_empty_qs", # noqa + "m2m_through.tests.M2mThroughTests.test_filter_on_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_get_on_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_get_or_create_on_m2m_with_intermediate_model_value_required_fails", # noqa + "m2m_through.tests.M2mThroughTests.test_order_by_relational_field_through_model", # noqa + "m2m_through.tests.M2mThroughTests.test_query_first_model_by_intermediate_model_attribute", # noqa + "m2m_through.tests.M2mThroughTests.test_query_model_by_attribute_name_of_related_model", # noqa + "m2m_through.tests.M2mThroughTests.test_query_model_by_custom_related_name", # noqa + "m2m_through.tests.M2mThroughTests.test_query_model_by_intermediate_can_return_non_unique_queryset", # noqa + "m2m_through.tests.M2mThroughTests.test_query_model_by_related_model_name", # noqa + "m2m_through.tests.M2mThroughTests.test_query_second_model_by_intermediate_model_attribute", # noqa + "m2m_through.tests.M2mThroughTests.test_remove_on_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_remove_on_reverse_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_retrieve_intermediate_items", # noqa + "m2m_through.tests.M2mThroughTests.test_retrieve_reverse_intermediate_items", # noqa + "m2m_through.tests.M2mThroughTests.test_set_on_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_set_on_m2m_with_intermediate_model_value_required_fails", # noqa + "m2m_through.tests.M2mThroughTests.test_set_on_reverse_m2m_with_intermediate_model", # noqa + "m2m_through.tests.M2mThroughTests.test_update_or_create_on_m2m_with_intermediate_model_value_required_fails", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_join_trimming_forwards", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_join_trimming_reverse", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_retrieve_forward_m2m_items", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_retrieve_forward_m2m_items_via_custom_id_intermediary", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_retrieve_reverse_m2m_items", # noqa + "m2m_through_regress.tests.M2MThroughTestCase.test_retrieve_reverse_m2m_items_via_custom_id_intermediary", # noqa + "m2m_through_regress.tests.ThroughLoadDataTestCase.test_sequence_creation", # noqa + "m2m_through_regress.tests.ToFieldThroughTests.test_add_null_reverse", # noqa + "m2m_through_regress.tests.ToFieldThroughTests.test_add_null_reverse_related", # noqa + "m2m_through_regress.tests.ToFieldThroughTests.test_add_related_null", # noqa + "m2o_recursive.tests.ManyToOneRecursiveTests.test_m2o_recursive", # noqa + "m2o_recursive.tests.MultipleManyToOneRecursiveTests.test_m2o_recursive2", # noqa + "managers_regress.tests.ManagersRegressionTests.test_field_can_be_called_exact", # noqa + "managers_regress.tests.ManagersRegressionTests.test_regress_3871", # noqa + "many_to_one.tests.ManyToOneTests.test_add_after_prefetch", # noqa + "many_to_one.tests.ManyToOneTests.test_add_then_remove_after_prefetch", # noqa + "many_to_one.tests.ManyToOneTests.test_cached_foreign_key_with_to_field_not_cleared_by_save", # noqa + "many_to_one.tests.ManyToOneTests.test_multiple_foreignkeys", # noqa + "many_to_one.tests.ManyToOneTests.test_reverse_foreign_key_instance_to_field_caching", # noqa + "many_to_one.tests.ManyToOneTests.test_set_after_prefetch", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_add_efficiency", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_assign_clear_related_set", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_assign_with_queryset", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_clear_efficiency", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_created_via_related_set", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_created_without_related", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_get_related", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_related_null_to_field", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_related_set", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_remove_from_wrong_set", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_set", # noqa + "many_to_one_null.tests.ManyToOneNullTests.test_set_clear_non_bulk", # noqa + "migrations.test_operations.OperationTests.test_add_binaryfield", # noqa + "migrations.test_operations.OperationTests.test_add_charfield", # noqa + "migrations.test_operations.OperationTests.test_add_constraint", # noqa + "migrations.test_operations.OperationTests.test_add_constraint_percent_escaping", # noqa + "migrations.test_operations.OperationTests.test_add_field", # noqa + "migrations.test_operations.OperationTests.test_add_field_m2m", # noqa + "migrations.test_operations.OperationTests.test_add_field_preserve_default", # noqa + "migrations.test_operations.OperationTests.test_add_index", # noqa + "migrations.test_operations.OperationTests.test_add_index_state_forwards", # noqa + "migrations.test_operations.OperationTests.test_add_or_constraint", # noqa + "migrations.test_operations.OperationTests.test_add_partial_unique_constraint", # noqa + "migrations.test_operations.OperationTests.test_add_textfield", # noqa + "migrations.test_operations.OperationTests.test_alter_field", # noqa + "migrations.test_operations.OperationTests.test_alter_field_m2m", # noqa + "migrations.test_operations.OperationTests.test_alter_field_pk", # noqa + "migrations.test_operations.OperationTests.test_alter_field_pk_fk", # noqa + "migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_target_changes", # noqa + "migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_with_to_field_target_changes", # noqa + "migrations.test_operations.OperationTests.test_alter_fk", # noqa + "migrations.test_operations.OperationTests.test_alter_index_together", # noqa + "migrations.test_operations.OperationTests.test_alter_index_together_remove", # noqa + "migrations.test_operations.OperationTests.test_alter_model_managers", # noqa + "migrations.test_operations.OperationTests.test_alter_model_managers_emptying", # noqa + "migrations.test_operations.OperationTests.test_alter_model_options", # noqa + "migrations.test_operations.OperationTests.test_alter_model_options_emptying", # noqa + "migrations.test_operations.OperationTests.test_alter_model_table_none", # noqa + "migrations.test_operations.OperationTests.test_alter_model_table_noop", # noqa + "migrations.test_operations.OperationTests.test_alter_unique_together", # noqa + "migrations.test_operations.OperationTests.test_alter_unique_together_remove", # noqa + "migrations.test_operations.OperationTests.test_autofield_foreignfield_growth", # noqa + "migrations.test_operations.OperationTests.test_column_name_quoting", # noqa + "migrations.test_operations.OperationTests.test_create_model", # noqa + "migrations.test_operations.OperationTests.test_create_model_inheritance", # noqa + "migrations.test_operations.OperationTests.test_create_model_m2m", # noqa + "migrations.test_operations.OperationTests.test_create_model_managers", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_constraint", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_duplicate_base", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_duplicate_field_name", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_duplicate_manager_name", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_partial_unique_constraint", # noqa + "migrations.test_operations.OperationTests.test_create_model_with_unique_after", # noqa + "migrations.test_operations.OperationTests.test_create_proxy_model", # noqa + "migrations.test_operations.OperationTests.test_create_unmanaged_model", # noqa + "migrations.test_operations.OperationTests.test_delete_model", # noqa + "migrations.test_operations.OperationTests.test_delete_mti_model", # noqa + "migrations.test_operations.OperationTests.test_delete_proxy_model", # noqa + "migrations.test_operations.OperationTests.test_model_with_bigautofield", # noqa + "migrations.test_operations.OperationTests.test_remove_constraint", # noqa + "migrations.test_operations.OperationTests.test_remove_field", # noqa + "migrations.test_operations.OperationTests.test_remove_field_m2m", # noqa + "migrations.test_operations.OperationTests.test_remove_field_m2m_with_through", # noqa + "migrations.test_operations.OperationTests.test_remove_fk", # noqa + "migrations.test_operations.OperationTests.test_remove_index", # noqa + "migrations.test_operations.OperationTests.test_remove_index_state_forwards", # noqa + "migrations.test_operations.OperationTests.test_remove_partial_unique_constraint", # noqa + "migrations.test_operations.OperationTests.test_rename_missing_field", # noqa + "migrations.test_operations.OperationTests.test_rename_model_state_forwards", # noqa + "migrations.test_operations.OperationTests.test_rename_referenced_field_state_forward", # noqa + "migrations.test_operations.OperationTests.test_run_python", # noqa + "migrations.test_operations.OperationTests.test_run_python_atomic", # noqa + "migrations.test_operations.OperationTests.test_run_python_noop", # noqa + "migrations.test_operations.OperationTests.test_run_python_related_assignment", # noqa + "migrations.test_operations.OperationTests.test_run_sql", # noqa + "migrations.test_operations.OperationTests.test_run_sql_noop", # noqa + "migrations.test_operations.OperationTests.test_run_sql_params_invalid", # noqa + "migrations.test_operations.OperationTests.test_separate_database_and_state", # noqa + "migrations.test_operations.OperationTests.test_separate_database_and_state2", # noqa + "model_fields.test_booleanfield.BooleanFieldTests.test_null_default", # noqa + "model_fields.test_durationfield.TestSaveLoad.test_create_empty", # noqa + "model_fields.test_genericipaddressfield.GenericIPAddressFieldTests.test_blank_string_saved_as_null", # noqa + "model_fields.test_genericipaddressfield.GenericIPAddressFieldTests.test_null_value", # noqa + "model_fields.test_imagefield.TwoImageFieldTests.test_dimensions", # noqa + "model_fields.test_imagefield.TwoImageFieldTests.test_field_save_and_delete_methods", # noqa + "model_fields.test_integerfield.BigIntegerFieldTests.test_backend_range_save", # noqa + "model_fields.test_integerfield.BigIntegerFieldTests.test_coercing", # noqa + "model_fields.test_integerfield.BigIntegerFieldTests.test_documented_range", # noqa + "model_fields.test_integerfield.BigIntegerFieldTests.test_types", # noqa + "model_fields.test_uuid.TestQuerying.test_exact", # noqa + "model_fields.test_uuid.TestQuerying.test_isnull", # noqa + "model_fields.test_uuid.TestSaveLoad.test_null_handling", # noqa + "multiple_database.tests.FixtureTestCase.test_fixture_loading", # noqa + "multiple_database.tests.FixtureTestCase.test_pseudo_empty_fixtures", # noqa + "multiple_database.tests.PickleQuerySetTestCase.test_pickling", # noqa + "multiple_database.tests.QueryTestCase.test_basic_queries", # noqa + "multiple_database.tests.QueryTestCase.test_default_creation", # noqa + "multiple_database.tests.QueryTestCase.test_foreign_key_cross_database_protection", # noqa + "multiple_database.tests.QueryTestCase.test_foreign_key_reverse_operations", # noqa + "multiple_database.tests.QueryTestCase.test_foreign_key_separation", # noqa + "multiple_database.tests.QueryTestCase.test_generic_key_cross_database_protection", # noqa + "multiple_database.tests.QueryTestCase.test_generic_key_deletion", # noqa + "multiple_database.tests.QueryTestCase.test_generic_key_reverse_operations", # noqa + "multiple_database.tests.QueryTestCase.test_generic_key_separation", # noqa + "multiple_database.tests.QueryTestCase.test_m2m_cross_database_protection", # noqa + "multiple_database.tests.QueryTestCase.test_m2m_deletion", # noqa + "multiple_database.tests.QueryTestCase.test_m2m_forward_operations", # noqa + "multiple_database.tests.QueryTestCase.test_m2m_reverse_operations", # noqa + "multiple_database.tests.QueryTestCase.test_m2m_separation", # noqa + "multiple_database.tests.QueryTestCase.test_o2o_cross_database_protection", # noqa + "multiple_database.tests.QueryTestCase.test_o2o_separation", # noqa + "multiple_database.tests.QueryTestCase.test_ordering", # noqa + "multiple_database.tests.QueryTestCase.test_other_creation", # noqa + "multiple_database.tests.QueryTestCase.test_raw", # noqa + "multiple_database.tests.QueryTestCase.test_refresh", # noqa + "multiple_database.tests.QueryTestCase.test_refresh_router_instance_hint", # noqa + "multiple_database.tests.QueryTestCase.test_related_manager", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_add", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_clear", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_delete", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_get_or_create", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_remove", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_m2m_update", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_add", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_clear", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_delete", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_get_or_create", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_remove", # noqa + "multiple_database.tests.RouteForWriteTestCase.test_reverse_m2m_update", # noqa + "multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_delete", # noqa + "multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_m2m", # noqa + "multiple_database.tests.RouterAttributeErrorTestCase.test_attribute_error_read", # noqa + "multiple_database.tests.RouterModelArgumentTestCase.test_m2m_collection", # noqa + "multiple_database.tests.RouterTestCase.test_database_routing", # noqa + "multiple_database.tests.RouterTestCase.test_foreign_key_cross_database_protection", # noqa + "multiple_database.tests.RouterTestCase.test_generic_key_managers", # noqa + "multiple_database.tests.RouterTestCase.test_invalid_set_foreign_key_assignment", # noqa + "multiple_database.tests.RouterTestCase.test_m2m_cross_database_protection", # noqa + "multiple_database.tests.RouterTestCase.test_m2m_managers", # noqa + "multiple_database.tests.RouterTestCase.test_o2o_cross_database_protection", # noqa + "multiple_database.tests.RouterTestCase.test_partial_router", # noqa + "multiple_database.tests.SignalTests.test_database_arg_m2m", # noqa + "null_fk.tests.NullFkTests.test_combine_isnull", # noqa + "null_fk.tests.NullFkTests.test_null_fk", # noqa + "null_fk_ordering.tests.NullFkOrderingTests.test_ordering_across_null_fk", # noqa + "null_queries.tests.NullQueriesTests.test_reverse_relations", # noqa + "ordering.tests.OrderingTests.test_default_ordering", # noqa + "ordering.tests.OrderingTests.test_default_ordering_override", # noqa + "ordering.tests.OrderingTests.test_deprecated_values_annotate", # noqa + "ordering.tests.OrderingTests.test_extra_ordering", # noqa + "ordering.tests.OrderingTests.test_extra_ordering_quoting", # noqa + "ordering.tests.OrderingTests.test_extra_ordering_with_table_name", # noqa + "ordering.tests.OrderingTests.test_no_reordering_after_slicing", # noqa + "ordering.tests.OrderingTests.test_order_by_f_expression", # noqa + "ordering.tests.OrderingTests.test_order_by_f_expression_duplicates", # noqa + "ordering.tests.OrderingTests.test_order_by_nulls_first", # noqa + "ordering.tests.OrderingTests.test_order_by_nulls_first_and_last", # noqa + "ordering.tests.OrderingTests.test_order_by_nulls_last", # noqa + "ordering.tests.OrderingTests.test_orders_nulls_first_on_filtered_subquery", # noqa + "ordering.tests.OrderingTests.test_related_ordering_duplicate_table_reference", # noqa + "ordering.tests.OrderingTests.test_reverse_ordering_pure", # noqa + "ordering.tests.OrderingTests.test_reversed_ordering", # noqa + "ordering.tests.OrderingTests.test_stop_slicing", # noqa + "ordering.tests.OrderingTests.test_stop_start_slicing", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_batch_size", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_foreign_keys_do_not_lookup", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_functions", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_set_field_to_null", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_set_mixed_fields_to_null", # noqa + "queries.test_bulk_update.BulkUpdateNoteTests.test_simple", # noqa + "queries.test_bulk_update.BulkUpdateTests.test_custom_db_columns", # noqa + "queries.test_bulk_update.BulkUpdateTests.test_field_references", # noqa + "queries.test_bulk_update.BulkUpdateTests.test_ipaddressfield", # noqa + "queries.tests.CloneTests.test_evaluated_queryset_as_argument", # noqa + "queries.tests.ComparisonTests.test_ticket8597", # noqa + "queries.tests.ConditionalTests.test_in_list_limit", # noqa + "queries.tests.ConditionalTests.test_infinite_loop", # noqa + "queries.tests.ConditionalTests.test_null_ordering_added", # noqa + "queries.tests.DisjunctionPromotionTests.test_disjunction_promotion_select_related", # noqa + "queries.tests.DisjunctiveFilterTests.test_ticket7872", # noqa + "queries.tests.DisjunctiveFilterTests.test_ticket8283", # noqa + "queries.tests.IsNullTests.test_primary_key", # noqa + "queries.tests.IsNullTests.test_to_field", # noqa + "queries.tests.JoinReuseTest.test_inverted_q_across_relations", # noqa + "queries.tests.NullInExcludeTest.test_col_not_in_list_containing_null", # noqa + "queries.tests.NullInExcludeTest.test_double_exclude", # noqa + "queries.tests.NullInExcludeTest.test_null_in_exclude_qs", # noqa + "queries.tests.NullJoinPromotionOrTest.test_isnull_filter_promotion", # noqa + "queries.tests.NullJoinPromotionOrTest.test_null_join_demotion", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_17886", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_21366", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_21748", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_21748_complex_filter", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_21748_double_negated_and", # noqa + "queries.tests.NullJoinPromotionOrTest.test_ticket_21748_double_negated_or", # noqa + "queries.tests.NullableRelOrderingTests.test_join_already_in_query", # noqa + "queries.tests.NullableRelOrderingTests.test_ticket10028", # noqa + "queries.tests.Queries1Tests.test_avoid_infinite_loop_on_too_many_subqueries", # noqa + "queries.tests.Queries1Tests.test_common_mixed_case_foreign_keys", # noqa + "queries.tests.Queries1Tests.test_deferred_load_qs_pickling", # noqa + "queries.tests.Queries1Tests.test_double_exclude", # noqa + "queries.tests.Queries1Tests.test_error_raised_on_filter_with_dictionary", # noqa + "queries.tests.Queries1Tests.test_exclude", # noqa + "queries.tests.Queries1Tests.test_exclude_in", # noqa + "queries.tests.Queries1Tests.test_get_clears_ordering", # noqa + "queries.tests.Queries1Tests.test_heterogeneous_qs_combination", # noqa + "queries.tests.Queries1Tests.test_lookup_constraint_fielderror", # noqa + "queries.tests.Queries1Tests.test_nested_exclude", # noqa + "queries.tests.Queries1Tests.test_order_by_join_unref", # noqa + "queries.tests.Queries1Tests.test_order_by_tables", # noqa + "queries.tests.Queries1Tests.test_reasonable_number_of_subq_aliases", # noqa + "queries.tests.Queries1Tests.test_subquery_condition", # noqa + "queries.tests.Queries1Tests.test_ticket10205", # noqa + "queries.tests.Queries1Tests.test_ticket10432", # noqa + "queries.tests.Queries1Tests.test_ticket1050", # noqa + "queries.tests.Queries1Tests.test_ticket10742", # noqa + "queries.tests.Queries1Tests.test_ticket17429", # noqa + "queries.tests.Queries1Tests.test_ticket1801", # noqa + "queries.tests.Queries1Tests.test_ticket19672", # noqa + "queries.tests.Queries1Tests.test_ticket2091", # noqa + "queries.tests.Queries1Tests.test_ticket2253", # noqa + "queries.tests.Queries1Tests.test_ticket2306", # noqa + "queries.tests.Queries1Tests.test_ticket2400", # noqa + "queries.tests.Queries1Tests.test_ticket2496", # noqa + "queries.tests.Queries1Tests.test_ticket2902", # noqa + "queries.tests.Queries1Tests.test_ticket3037", # noqa + "queries.tests.Queries1Tests.test_ticket3141", # noqa + "queries.tests.Queries1Tests.test_ticket4358", # noqa + "queries.tests.Queries1Tests.test_ticket4464", # noqa + "queries.tests.Queries1Tests.test_ticket4510", # noqa + "queries.tests.Queries1Tests.test_ticket6074", # noqa + "queries.tests.Queries1Tests.test_ticket6154", # noqa + "queries.tests.Queries1Tests.test_ticket6981", # noqa + "queries.tests.Queries1Tests.test_ticket7076", # noqa + "queries.tests.Queries1Tests.test_ticket7096", # noqa + "queries.tests.Queries1Tests.test_ticket7098", # noqa + "queries.tests.Queries1Tests.test_ticket7155", # noqa + "queries.tests.Queries1Tests.test_ticket7181", # noqa + "queries.tests.Queries1Tests.test_ticket7235", # noqa + "queries.tests.Queries1Tests.test_ticket7277", # noqa + "queries.tests.Queries1Tests.test_ticket7323", # noqa + "queries.tests.Queries1Tests.test_ticket7378", # noqa + "queries.tests.Queries1Tests.test_ticket7791", # noqa + "queries.tests.Queries1Tests.test_ticket7813", # noqa + "queries.tests.Queries1Tests.test_ticket8439", # noqa + "queries.tests.Queries1Tests.test_ticket9926", # noqa + "queries.tests.Queries1Tests.test_ticket9985", # noqa + "queries.tests.Queries1Tests.test_ticket9997", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_1", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_2", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_3", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_4", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_5", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_6", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_7", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_8", # noqa + "queries.tests.Queries1Tests.test_ticket_10790_combine", # noqa + "queries.tests.Queries1Tests.test_ticket_20250", # noqa + "queries.tests.Queries1Tests.test_tickets_1878_2939", # noqa + "queries.tests.Queries1Tests.test_tickets_2076_7256", # noqa + "queries.tests.Queries1Tests.test_tickets_2080_3592", # noqa + "queries.tests.Queries1Tests.test_tickets_2874_3002", # noqa + "queries.tests.Queries1Tests.test_tickets_4088_4306", # noqa + "queries.tests.Queries1Tests.test_tickets_5321_7070", # noqa + "queries.tests.Queries1Tests.test_tickets_5324_6704", # noqa + "queries.tests.Queries1Tests.test_tickets_6180_6203", # noqa + "queries.tests.Queries1Tests.test_tickets_7087_12242", # noqa + "queries.tests.Queries1Tests.test_tickets_7204_7506", # noqa + "queries.tests.Queries1Tests.test_tickets_7448_7707", # noqa + "queries.tests.Queries2Tests.test_ticket12239", # noqa + "queries.tests.Queries2Tests.test_ticket4289", # noqa + "queries.tests.Queries2Tests.test_ticket7759", # noqa + "queries.tests.Queries4Tests.test_combine_join_reuse", # noqa + "queries.tests.Queries4Tests.test_join_reuse_order", # noqa + "queries.tests.Queries4Tests.test_order_by_resetting", # noqa + "queries.tests.Queries4Tests.test_order_by_reverse_fk", # noqa + "queries.tests.Queries4Tests.test_ticket10181", # noqa + "queries.tests.Queries4Tests.test_ticket11811", # noqa + "queries.tests.Queries4Tests.test_ticket14876", # noqa + "queries.tests.Queries4Tests.test_ticket15316_exclude_false", # noqa + "queries.tests.Queries4Tests.test_ticket15316_filter_false", # noqa + "queries.tests.Queries4Tests.test_ticket15316_filter_true", # noqa + "queries.tests.Queries4Tests.test_ticket15316_one2one_exclude_false", # noqa + "queries.tests.Queries4Tests.test_ticket15316_one2one_exclude_true", # noqa + "queries.tests.Queries4Tests.test_ticket15316_one2one_filter_false", # noqa + "queries.tests.Queries4Tests.test_ticket15316_one2one_filter_true", # noqa + "queries.tests.Queries4Tests.test_ticket24525", # noqa + "queries.tests.Queries4Tests.test_ticket7095", # noqa + "queries.tests.Queries5Tests.test_extra_select_literal_percent_s", # noqa + "queries.tests.Queries5Tests.test_ordering", # noqa + "queries.tests.Queries5Tests.test_ticket5261", # noqa + "queries.tests.Queries5Tests.test_ticket7045", # noqa + "queries.tests.Queries5Tests.test_ticket9848", # noqa + "queries.tests.Queries6Tests.test_distinct_ordered_sliced_subquery_aggregation", # noqa + "queries.tests.Queries6Tests.test_multiple_columns_with_the_same_name_slice", # noqa + "queries.tests.Queries6Tests.test_nested_queries_sql", # noqa + "queries.tests.Queries6Tests.test_parallel_iterators", # noqa + "queries.tests.Queries6Tests.test_ticket3739", # noqa + "queries.tests.Queries6Tests.test_ticket_11320", # noqa + "queries.tests.Queries6Tests.test_tickets_8921_9188", # noqa + "queries.tests.RawQueriesTests.test_ticket14729", # noqa + "queries.tests.RelabelCloneTest.test_ticket_19964", # noqa + "queries.tests.RelatedLookupTypeTests.test_correct_lookup", # noqa + "queries.tests.RelatedLookupTypeTests.test_wrong_backward_lookup", # noqa + "queries.tests.RelatedLookupTypeTests.test_wrong_type_lookup", # noqa + "queries.tests.ReverseJoinTrimmingTest.test_reverse_trimming", # noqa + "queries.tests.SubclassFKTests.test_ticket7778", # noqa + "queries.tests.Ticket20101Tests.test_ticket_20101", # noqa + "queries.tests.Ticket22429Tests.test_ticket_22429", # noqa + "queries.tests.ToFieldTests.test_nested_in_subquery", # noqa + "queries.tests.ToFieldTests.test_recursive_fk", # noqa + "queries.tests.ToFieldTests.test_recursive_fk_reverse", # noqa + "queries.tests.ValuesJoinPromotionTests.test_ticket_21376", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_multiple_select_params_values_order_by", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_select_params_values_order_in_extra", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_values", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_values_list", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_values_order_in_extra", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_values_order_multiple", # noqa + "queries.tests.ValuesQuerysetTests.test_extra_values_order_twice", # noqa + "queries.tests.ValuesQuerysetTests.test_field_error_values_list", # noqa + "queries.tests.ValuesQuerysetTests.test_flat_extra_values_list", # noqa + "queries.tests.ValuesQuerysetTests.test_flat_values_list", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_bad_field_name", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_expression", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_expression_with_default_alias", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_flat", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_with_fields", # noqa + "queries.tests.ValuesQuerysetTests.test_named_values_list_without_fields", # noqa + "queries.tests.WeirdQuerysetSlicingTests.test_empty_resultset_sql", # noqa + "queries.tests.WeirdQuerysetSlicingTests.test_empty_sliced_subquery", # noqa + "queries.tests.WeirdQuerysetSlicingTests.test_empty_sliced_subquery_exclude", # noqa + "queries.tests.WeirdQuerysetSlicingTests.test_tickets_7698_10202", # noqa + "queries.tests.WeirdQuerysetSlicingTests.test_zero_length_values_slicing", # noqa + "schema.tests.SchemaTests.test_add_datefield_and_datetimefield_use_effective_default", # noqa + "schema.tests.SchemaTests.test_add_field", # noqa + "schema.tests.SchemaTests.test_add_field_binary", # noqa + "schema.tests.SchemaTests.test_add_field_default_dropped", # noqa + "schema.tests.SchemaTests.test_add_field_default_transform", # noqa + "schema.tests.SchemaTests.test_add_field_remove_field", # noqa + "schema.tests.SchemaTests.test_add_field_temp_default", # noqa + "schema.tests.SchemaTests.test_add_field_temp_default_boolean", # noqa + "schema.tests.SchemaTests.test_add_field_use_effective_default", # noqa + "schema.tests.SchemaTests.test_add_foreign_key_long_names", # noqa + "schema.tests.SchemaTests.test_add_foreign_key_quoted_db_table", # noqa + "schema.tests.SchemaTests.test_add_foreign_object", # noqa + "schema.tests.SchemaTests.test_add_remove_index", # noqa + "schema.tests.SchemaTests.test_add_textfield_unhashable_default", # noqa + "schema.tests.SchemaTests.test_alter", # noqa + "schema.tests.SchemaTests.test_alter_auto_field_to_integer_field", # noqa + "schema.tests.SchemaTests.test_alter_charfield_to_null", # noqa + "schema.tests.SchemaTests.test_alter_field_add_index_to_integerfield", # noqa + "schema.tests.SchemaTests.test_alter_field_default_doesnt_perfom_queries", # noqa + "schema.tests.SchemaTests.test_alter_field_default_dropped", # noqa + "schema.tests.SchemaTests.test_alter_field_fk_keeps_index", # noqa + "schema.tests.SchemaTests.test_alter_field_fk_to_o2o", # noqa + "schema.tests.SchemaTests.test_alter_field_o2o_keeps_unique", # noqa + "schema.tests.SchemaTests.test_alter_field_o2o_to_fk", # noqa + "schema.tests.SchemaTests.test_alter_fk", # noqa + "schema.tests.SchemaTests.test_alter_fk_checks_deferred_constraints", # noqa + "schema.tests.SchemaTests.test_alter_fk_to_o2o", # noqa + "schema.tests.SchemaTests.test_alter_implicit_id_to_explicit", # noqa + "schema.tests.SchemaTests.test_alter_int_pk_to_autofield_pk", # noqa + "schema.tests.SchemaTests.test_alter_int_pk_to_bigautofield_pk", # noqa + "schema.tests.SchemaTests.test_alter_null_to_not_null", # noqa + "schema.tests.SchemaTests.test_alter_null_to_not_null_keeping_default", # noqa + "schema.tests.SchemaTests.test_alter_numeric_field_keep_null_status", # noqa + "schema.tests.SchemaTests.test_alter_o2o_to_fk", # noqa + "schema.tests.SchemaTests.test_alter_text_field", # noqa + "schema.tests.SchemaTests.test_alter_textfield_to_null", # noqa + "schema.tests.SchemaTests.test_alter_textual_field_keep_null_status", # noqa + "schema.tests.SchemaTests.test_alter_to_fk", # noqa + "schema.tests.SchemaTests.test_char_field_with_db_index_to_fk", # noqa + "schema.tests.SchemaTests.test_check_constraints", # noqa + "schema.tests.SchemaTests.test_context_manager_exit", # noqa + "schema.tests.SchemaTests.test_create_index_together", # noqa + "schema.tests.SchemaTests.test_creation_deletion", # noqa + "schema.tests.SchemaTests.test_creation_deletion_reserved_names", # noqa + "schema.tests.SchemaTests.test_fk", # noqa + "schema.tests.SchemaTests.test_fk_db_constraint", # noqa + "schema.tests.SchemaTests.test_fk_to_proxy", # noqa + "schema.tests.SchemaTests.test_foreign_key_index_long_names_regression", # noqa + "schema.tests.SchemaTests.test_index_together", # noqa + "schema.tests.SchemaTests.test_index_together_with_fk", # noqa + "schema.tests.SchemaTests.test_indexes", # noqa + "schema.tests.SchemaTests.test_m2m", # noqa + "schema.tests.SchemaTests.test_m2m_create", # noqa + "schema.tests.SchemaTests.test_m2m_create_custom", # noqa + "schema.tests.SchemaTests.test_m2m_create_inherited", # noqa + "schema.tests.SchemaTests.test_m2m_create_through", # noqa + "schema.tests.SchemaTests.test_m2m_create_through_custom", # noqa + "schema.tests.SchemaTests.test_m2m_create_through_inherited", # noqa + "schema.tests.SchemaTests.test_m2m_custom", # noqa + "schema.tests.SchemaTests.test_m2m_db_constraint", # noqa + "schema.tests.SchemaTests.test_m2m_db_constraint_custom", # noqa + "schema.tests.SchemaTests.test_m2m_db_constraint_inherited", # noqa + "schema.tests.SchemaTests.test_m2m_inherited", # noqa + "schema.tests.SchemaTests.test_m2m_through_alter", # noqa + "schema.tests.SchemaTests.test_m2m_through_alter_custom", # noqa + "schema.tests.SchemaTests.test_m2m_through_alter_inherited", # noqa + "schema.tests.SchemaTests.test_namespaced_db_table_create_index_name", # noqa + "schema.tests.SchemaTests.test_no_db_constraint_added_during_primary_key_change", # noqa + "schema.tests.SchemaTests.test_order_index", # noqa + "schema.tests.SchemaTests.test_remove_constraints_capital_letters", # noqa + "schema.tests.SchemaTests.test_remove_db_index_doesnt_remove_custom_indexes", # noqa + "schema.tests.SchemaTests.test_remove_field_check_does_not_remove_meta_constraints", # noqa + "schema.tests.SchemaTests.test_remove_field_unique_does_not_remove_meta_constraints", # noqa + "schema.tests.SchemaTests.test_remove_index_together_does_not_remove_meta_indexes", # noqa + "schema.tests.SchemaTests.test_remove_unique_together_does_not_remove_meta_constraints", # noqa + "schema.tests.SchemaTests.test_text_field_with_db_index", # noqa + "schema.tests.SchemaTests.test_text_field_with_db_index_to_fk", # noqa + "schema.tests.SchemaTests.test_unique", # noqa + "schema.tests.SchemaTests.test_unique_and_reverse_m2m", # noqa + "schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops", # noqa + "schema.tests.SchemaTests.test_unique_together", # noqa + "schema.tests.SchemaTests.test_unique_together_with_fk", # noqa + "schema.tests.SchemaTests.test_unique_together_with_fk_with_existing_index", # noqa + "schema.tests.SchemaTests.test_unsupported_transactional_ddl_disallowed", # noqa + "select_related_onetoone.tests.ReverseSelectRelatedTestCase.test_nullable_relation", # noqa + "select_related_onetoone.tests.ReverseSelectRelatedTestCase.test_self_relation", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_actual_expiry", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_clearsessions_command", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_cycle", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_cycle_with_no_session_cache", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_delete", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_flush", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_invalid_key", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_save", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_save_doesnt_clear_data", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_session_get_decoded", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_session_save_does_not_resurrect_session_logged_out_in_other_context", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_session_str", # noqa + "sessions_tests.tests.CustomDatabaseSessionTests.test_sessionmanager_save", # noqa + "sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap", # noqa + "sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap_attributes", # noqa + "sitemaps_tests.test_generic.GenericViewsSitemapTests.test_generic_sitemap_lastmod", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_cached_sitemap_index", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_empty_page", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_empty_sitemap", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_localized_priority", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_no_section", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_page_not_int", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_paged_sitemap", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_requestsite_sitemap", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_custom_sitemap", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_i18nsitemap_index", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_custom_index", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_index", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_simple_sitemap_section", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_get_urls_no_site_1", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_get_urls_no_site_2", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_item", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_date", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_missing", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_mixed", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_last_modified_tz", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_not_callable", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemap_without_entries", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_ascending", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_descending", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_mixed_ascending_last_modified_missing", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_sitemaps_lastmod_mixed_descending_last_modified_missing", # noqa + "sitemaps_tests.test_http.HTTPSitemapTests.test_x_robots_sitemap", # noqa + "sitemaps_tests.test_https.HTTPSDetectionSitemapTests.test_sitemap_index_with_https_request", # noqa + "sitemaps_tests.test_https.HTTPSDetectionSitemapTests.test_sitemap_section_with_https_request", # noqa + "sitemaps_tests.test_https.HTTPSSitemapTests.test_secure_sitemap_index", # noqa + "sitemaps_tests.test_https.HTTPSSitemapTests.test_secure_sitemap_section", # noqa + "sitemaps_tests.test_management.PingGoogleTests.test_args", # noqa + "sitemaps_tests.test_management.PingGoogleTests.test_default", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_exact_url", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_global", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_index", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_insecure", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_no_sites", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_get_sitemap_full_url_not_detected", # noqa + "sitemaps_tests.test_utils.PingGoogleTests.test_something", # noqa + "string_lookup.tests.StringLookupTests.test_queries_on_textfields", # noqa + "test_client.tests.ClientTest.test_empty_post", # noqa + "test_client.tests.ClientTest.test_exception_following_nested_client_request", # noqa + "test_client.tests.ClientTest.test_external_redirect", # noqa + "test_client.tests.ClientTest.test_external_redirect_with_fetch_error_msg", # noqa + "test_client.tests.ClientTest.test_follow_307_and_308_preserves_get_params", # noqa + "test_client.tests.ClientTest.test_follow_307_and_308_preserves_post_data", # noqa + "test_client.tests.ClientTest.test_follow_307_and_308_preserves_put_body", # noqa + "test_client.tests.ClientTest.test_follow_307_and_308_redirect", # noqa + "test_client.tests.ClientTest.test_follow_redirect", # noqa + "test_client.tests.ClientTest.test_follow_relative_redirect", # noqa + "test_client.tests.ClientTest.test_follow_relative_redirect_no_trailing_slash", # noqa + "test_client.tests.ClientTest.test_force_login_with_backend", # noqa + "test_client.tests.ClientTest.test_force_login_with_backend_missing_get_user", # noqa + "test_client.tests.ClientTest.test_force_login_without_backend", # noqa + "test_client.tests.ClientTest.test_form_error", # noqa + "test_client.tests.ClientTest.test_form_error_with_template", # noqa + "test_client.tests.ClientTest.test_get_data_none", # noqa + "test_client.tests.ClientTest.test_get_post_view", # noqa + "test_client.tests.ClientTest.test_get_view", # noqa + "test_client.tests.ClientTest.test_incomplete_data_form", # noqa + "test_client.tests.ClientTest.test_incomplete_data_form_with_template", # noqa + "test_client.tests.ClientTest.test_insecure", # noqa + "test_client.tests.ClientTest.test_json_encoder_argument", # noqa + "test_client.tests.ClientTest.test_json_serialization", # noqa + "test_client.tests.ClientTest.test_logout", # noqa + "test_client.tests.ClientTest.test_logout_cookie_sessions", # noqa + "test_client.tests.ClientTest.test_logout_with_force_login", # noqa + "test_client.tests.ClientTest.test_mail_sending", # noqa + "test_client.tests.ClientTest.test_mass_mail_sending", # noqa + "test_client.tests.ClientTest.test_notfound_response", # noqa + "test_client.tests.ClientTest.test_permanent_redirect", # noqa + "test_client.tests.ClientTest.test_post", # noqa + "test_client.tests.ClientTest.test_post_data_none", # noqa + "test_client.tests.ClientTest.test_put", # noqa + "test_client.tests.ClientTest.test_query_string_encoding", # noqa + "test_client.tests.ClientTest.test_raw_post", # noqa + "test_client.tests.ClientTest.test_redirect", # noqa + "test_client.tests.ClientTest.test_redirect_http", # noqa + "test_client.tests.ClientTest.test_redirect_https", # noqa + "test_client.tests.ClientTest.test_redirect_to_strange_location", # noqa + "test_client.tests.ClientTest.test_redirect_with_query", # noqa + "test_client.tests.ClientTest.test_redirect_with_query_ordering", # noqa + "test_client.tests.ClientTest.test_relative_redirect", # noqa + "test_client.tests.ClientTest.test_relative_redirect_no_trailing_slash", # noqa + "test_client.tests.ClientTest.test_response_attached_request", # noqa + "test_client.tests.ClientTest.test_response_headers", # noqa + "test_client.tests.ClientTest.test_response_raises_multi_arg_exception", # noqa + "test_client.tests.ClientTest.test_response_resolver_match", # noqa + "test_client.tests.ClientTest.test_response_resolver_match_redirect_follow", # noqa + "test_client.tests.ClientTest.test_response_resolver_match_regular_view", # noqa + "test_client.tests.ClientTest.test_reverse_lazy_decodes", # noqa + "test_client.tests.ClientTest.test_secure", # noqa + "test_client.tests.ClientTest.test_session_engine_is_invalid", # noqa + "test_client.tests.ClientTest.test_session_modifying_view", # noqa + "test_client.tests.ClientTest.test_sessions_app_is_not_installed", # noqa + "test_client.tests.ClientTest.test_temporary_redirect", # noqa + "test_client.tests.ClientTest.test_trace", # noqa + "test_client.tests.ClientTest.test_unknown_page", # noqa + "test_client.tests.ClientTest.test_uploading_named_temp_file", # noqa + "test_client.tests.ClientTest.test_uploading_temp_file", # noqa + "test_client.tests.ClientTest.test_url_parameters", # noqa + "test_client.tests.ClientTest.test_valid_form", # noqa + "test_client.tests.ClientTest.test_valid_form_with_hints", # noqa + "test_client.tests.ClientTest.test_valid_form_with_template", # noqa + "test_client.tests.ClientTest.test_view_with_bad_login", # noqa + "test_client.tests.ClientTest.test_view_with_exception", # noqa + "test_client.tests.ClientTest.test_view_with_force_login", # noqa + "test_client.tests.ClientTest.test_view_with_force_login_and_custom_redirect", # noqa + "test_client.tests.ClientTest.test_view_with_inactive_force_login", # noqa + "test_client.tests.ClientTest.test_view_with_inactive_login", # noqa + "test_client.tests.ClientTest.test_view_with_login", # noqa + "test_client.tests.ClientTest.test_view_with_login_and_custom_redirect", # noqa + "test_client.tests.ClientTest.test_view_with_login_when_sessions_app_is_not_installed", # noqa + "test_client.tests.ClientTest.test_view_with_method_force_login", # noqa + "test_client.tests.ClientTest.test_view_with_method_login", # noqa + "test_client.tests.ClientTest.test_view_with_method_permissions", # noqa + "test_client.tests.ClientTest.test_view_with_permissions", # noqa + "test_client.tests.ClientTest.test_view_with_permissions_exception", # noqa + "test_client_regress.tests.AssertTemplateUsedTests.test_multiple_context", # noqa + "test_client_regress.tests.AssertTemplateUsedTests.test_no_context", # noqa + "test_client_regress.tests.AssertTemplateUsedTests.test_single_context", # noqa + "test_client_regress.tests.AssertTemplateUsedTests.test_template_rendered_multiple_times", # noqa + "test_client_regress.tests.ContextTests.test_15368", # noqa + "test_client_regress.tests.ContextTests.test_contextlist_get", # noqa + "test_client_regress.tests.ContextTests.test_contextlist_keys", # noqa + "test_client_regress.tests.ContextTests.test_inherited_context", # noqa + "test_client_regress.tests.ContextTests.test_nested_requests", # noqa + "test_client_regress.tests.ContextTests.test_single_context", # noqa + "test_client_regress.tests.ExceptionTests.test_exception_cleared", # noqa + "test_client_regress.tests.LoginTests.test_login_different_client", # noqa + "test_client_regress.tests.SessionEngineTests.test_login", # noqa + "test_client_regress.tests.SessionTests.test_login_with_user", # noqa + "test_client_regress.tests.SessionTests.test_login_without_signal", # noqa + "test_client_regress.tests.SessionTests.test_logout", # noqa + "test_client_regress.tests.SessionTests.test_logout_with_custom_auth_backend", # noqa + "test_client_regress.tests.SessionTests.test_logout_with_custom_user", # noqa + "test_client_regress.tests.SessionTests.test_logout_with_user", # noqa + "test_client_regress.tests.SessionTests.test_logout_without_user", # noqa + "test_client_regress.tests.SessionTests.test_session", # noqa + "test_client_regress.tests.SessionTests.test_session_initiated", # noqa + "timezones.tests.NewDatabaseTests.test_null_datetime", + "transactions.tests.NonAutocommitTests.test_orm_query_after_error_and_rollback", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_empty_update_fields", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_num_queries_inheritance", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_select_related_only_interaction", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_basic", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_fk_defer", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_incorrect_params", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance_defer", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_inheritance_with_proxy_model", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_m2m", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_only_1", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_only_repeated", # noqa + "update_only_fields.tests.UpdateOnlyFieldsTests.test_update_fields_signals", # noqa + "validation.tests.BaseModelValidationTests.test_correct_FK_value_validates", # noqa + "validation.tests.BaseModelValidationTests.test_limited_FK_raises_error", # noqa + "validation.tests.GenericIPAddressFieldTests.test_empty_generic_ip_passes", # noqa + "validation.tests.GenericIPAddressFieldTests.test_v4_unpack_uniqueness_detection", # noqa + "validation.tests.GenericIPAddressFieldTests.test_v6_uniqueness_detection", # noqa + ) diff --git a/django_spanner/functions.py b/google/cloud/spanner_django/functions.py similarity index 68% rename from django_spanner/functions.py rename to google/cloud/spanner_django/functions.py index f096187488..65c1d50d96 100644 --- a/django_spanner/functions.py +++ b/google/cloud/spanner_django/functions.py @@ -8,46 +8,67 @@ from django.db.models.expressions import Func, Value from django.db.models.functions import ( - Cast, Chr, ConcatPair, Cot, Degrees, Left, Log, Ord, Pi, Radians, Right, - StrIndex, Substr, + Cast, + Chr, + ConcatPair, + Cot, + Degrees, + Left, + Log, + Ord, + Pi, + Radians, + Right, + StrIndex, + Substr, ) class IfNull(Func): - function = 'IFNULL' + function = "IFNULL" arity = 2 def cast(self, compiler, connection, **extra_context): # Account for a field's max_length using SUBSTR. - max_length = getattr(self.output_field, 'max_length', None) + max_length = getattr(self.output_field, "max_length", None) if max_length is not None: - template = 'SUBSTR(' + self.template + ', 0, %s)' % max_length + template = "SUBSTR(" + self.template + ", 0, %s)" % max_length else: template = self.template return self.as_sql(compiler, connection, template=template, **extra_context) def chr_(self, compiler, connection, **extra_context): - return self.as_sql(compiler, connection, template='CODE_POINTS_TO_STRING([%(expressions)s])', **extra_context) + return self.as_sql( + compiler, + connection, + template="CODE_POINTS_TO_STRING([%(expressions)s])", + **extra_context + ) def concatpair(self, compiler, connection, **extra_context): # Spanner's CONCAT function returns null if any of its arguments are null. # Prevent that by converting null arguments to an empty string. clone = self.copy() - clone.set_source_expressions(IfNull(e, Value('')) for e in self.get_source_expressions()) + clone.set_source_expressions( + IfNull(e, Value("")) for e in self.get_source_expressions() + ) return clone.as_sql(compiler, connection, **extra_context) def cot(self, compiler, connection, **extra_context): - return self.as_sql(compiler, connection, template='(1 / TAN(%(expressions)s))', **extra_context) + return self.as_sql( + compiler, connection, template="(1 / TAN(%(expressions)s))", **extra_context + ) def degrees(self, compiler, connection, **extra_context): return self.as_sql( - compiler, connection, - template='((%%(expressions)s) * 180 / %s)' % math.pi, + compiler, + connection, + template="((%%(expressions)s) * 180 / %s)" % math.pi, **extra_context ) @@ -65,7 +86,12 @@ def log(self, compiler, connection, **extra_context): def ord_(self, compiler, connection, **extra_context): - return self.as_sql(compiler, connection, template='TO_CODE_POINTS(%(expressions)s)[OFFSET(0)]', **extra_context) + return self.as_sql( + compiler, + connection, + template="TO_CODE_POINTS(%(expressions)s)[OFFSET(0)]", + **extra_context + ) def pi(self, compiler, connection, **extra_context): @@ -74,18 +100,19 @@ def pi(self, compiler, connection, **extra_context): def radians(self, compiler, connection, **extra_context): return self.as_sql( - compiler, connection, - template='((%%(expressions)s) * %s / 180)' % math.pi, + compiler, + connection, + template="((%%(expressions)s) * %s / 180)" % math.pi, **extra_context ) def strindex(self, compiler, connection, **extra_context): - return self.as_sql(compiler, connection, function='STRPOS', **extra_context) + return self.as_sql(compiler, connection, function="STRPOS", **extra_context) def substr(self, compiler, connection, **extra_context): - return self.as_sql(compiler, connection, function='SUBSTR', **extra_context) + return self.as_sql(compiler, connection, function="SUBSTR", **extra_context) def register_functions(): diff --git a/django_spanner/introspection.py b/google/cloud/spanner_django/introspection.py similarity index 64% rename from django_spanner/introspection.py rename to google/cloud/spanner_django/introspection.py index 6dfa2e896c..c34da77142 100644 --- a/django_spanner/introspection.py +++ b/google/cloud/spanner_django/introspection.py @@ -5,7 +5,9 @@ # https://developers.google.com/open-source/licenses/bsd from django.db.backends.base.introspection import ( - BaseDatabaseIntrospection, FieldInfo, TableInfo, + BaseDatabaseIntrospection, + FieldInfo, + TableInfo, ) from django.db.models import Index from google.cloud.spanner_v1.proto import type_pb2 @@ -13,40 +15,42 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): data_types_reverse = { - type_pb2.BOOL: 'BooleanField', - type_pb2.BYTES: 'BinaryField', - type_pb2.DATE: 'DateField', - type_pb2.FLOAT64: 'FloatField', - type_pb2.INT64: 'IntegerField', - type_pb2.STRING: 'CharField', - type_pb2.TIMESTAMP: 'DateTimeField', + type_pb2.BOOL: "BooleanField", + type_pb2.BYTES: "BinaryField", + type_pb2.DATE: "DateField", + type_pb2.FLOAT64: "FloatField", + type_pb2.INT64: "IntegerField", + type_pb2.STRING: "CharField", + type_pb2.TIMESTAMP: "DateTimeField", } def get_field_type(self, data_type, description): - if data_type == type_pb2.STRING and description.internal_size == 'MAX': - return 'TextField' + if data_type == type_pb2.STRING and description.internal_size == "MAX": + return "TextField" return super().get_field_type(data_type, description) def get_table_list(self, cursor): """Return a list of table and view names in the current database.""" # The second TableInfo field is 't' for table or 'v' for view. - return [TableInfo(row[0], 't') for row in cursor.list_tables()] + return [TableInfo(row[0], "t") for row in cursor.list_tables()] def get_table_description(self, cursor, table_name): """ Return a description of the table with the DB-API cursor.description interface. """ - cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) + cursor.execute( + "SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name) + ) column_details = cursor.get_table_column_schema(table_name) descriptions = [] for line in cursor.description: column_name, type_code = line[0], line[1] details = column_details[column_name] - if details.spanner_type.startswith('STRING'): + if details.spanner_type.startswith("STRING"): # Extract the size of the string from, e.g. STRING(#). internal_size = details.spanner_type[7:-1] - if internal_size != 'MAX': + if internal_size != "MAX": internal_size = int(internal_size) else: internal_size = None @@ -89,13 +93,17 @@ def get_relations(self, cursor, table_name): ON rc.UNIQUE_CONSTRAINT_NAME = ccu.CONSTRAINT_NAME WHERE - tc.TABLE_NAME="%s"''' % self.connection.ops.quote_name(table_name), + tc.TABLE_NAME="%s"''' + % self.connection.ops.quote_name(table_name) ) - return {column: (referred_column, referred_table) for (column, referred_column, referred_table) in results} + return { + column: (referred_column, referred_table) + for (column, referred_column, referred_table) in results + } def get_primary_key_column(self, cursor, table_name): results = cursor.run_sql_in_snapshot( - ''' + """ SELECT ccu.COLUMN_NAME FROM @@ -106,7 +114,8 @@ def get_primary_key_column(self, cursor, table_name): ccu ON tc.CONSTRAINT_NAME = ccu.CONSTRAINT_NAME WHERE tc.TABLE_NAME="%s" AND tc.CONSTRAINT_TYPE='PRIMARY KEY' AND tc.TABLE_SCHEMA='' - ''' % self.connection.ops.quote_name(table_name), + """ + % self.connection.ops.quote_name(table_name) ) return results[0][0] if results else None @@ -121,22 +130,24 @@ def get_constraints(self, cursor, table_name): CONSTRAINT_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE - WHERE TABLE_NAME="{table}"'''.format(table=quoted_table_name), + WHERE TABLE_NAME="{table}"'''.format( + table=quoted_table_name + ) ) for constraint, column_name in constraint_columns: if constraint not in constraints: constraints[constraint] = { - 'check': False, - 'columns': [], - 'foreign_key': None, - 'index': False, - 'orders': [], - 'primary_key': False, - 'type': None, - 'unique': False, + "check": False, + "columns": [], + "foreign_key": None, + "index": False, + "orders": [], + "primary_key": False, + "type": None, + "unique": False, } - constraints[constraint]['columns'].append(column_name) + constraints[constraint]["columns"].append(column_name) # Add the various constraints by type. constraint_types = cursor.run_sql_in_snapshot( @@ -146,11 +157,13 @@ def get_constraints(self, cursor, table_name): FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE - TABLE_NAME="{table}"'''.format(table=quoted_table_name), + TABLE_NAME="{table}"'''.format( + table=quoted_table_name + ) ) for constraint, constraint_type in constraint_types: already_added = constraint in constraints - if constraint_type == 'FOREIGN KEY': + if constraint_type == "FOREIGN KEY": # We don't yet support anything related to FOREIGN KEY. # See https://github.com/orijtech/django-spanner/issues/313. if already_added: @@ -159,25 +172,27 @@ def get_constraints(self, cursor, table_name): if not already_added: constraints[constraint] = { - 'check': False, - 'columns': [], - 'foreign_key': None, - 'index': False, - 'orders': [], - 'primary_key': False, - 'type': None, - 'unique': False, + "check": False, + "columns": [], + "foreign_key": None, + "index": False, + "orders": [], + "primary_key": False, + "type": None, + "unique": False, } - is_primary_key = constraint_type == 'PRIMARY KEY' - constraints[constraint]['check'] = constraint_type == 'CHECK' - constraints[constraint]['index'] = constraint_type == 'INDEX' - constraints[constraint]['unique'] = constraint_type == 'UNIQUE' or is_primary_key - constraints[constraint]['primary_key'] = is_primary_key + is_primary_key = constraint_type == "PRIMARY KEY" + constraints[constraint]["check"] = constraint_type == "CHECK" + constraints[constraint]["index"] = constraint_type == "INDEX" + constraints[constraint]["unique"] = ( + constraint_type == "UNIQUE" or is_primary_key + ) + constraints[constraint]["primary_key"] = is_primary_key # Add the indices. indexes = cursor.run_sql_in_snapshot( - ''' + """ SELECT idx.INDEX_NAME, idx_col.COLUMN_NAME, idx_col.COLUMN_ORDERING, idx.INDEX_TYPE, idx.IS_UNIQUE FROM @@ -190,28 +205,32 @@ def get_constraints(self, cursor, table_name): idx.TABLE_NAME="{table}" ORDER BY idx_col.ORDINAL_POSITION - '''.format(table=quoted_table_name), + """.format( + table=quoted_table_name + ) ) for index_name, column_name, ordering, index_type, is_unique in indexes: if index_name not in constraints: constraints[index_name] = { - 'check': False, - 'columns': [], - 'foreign_key': None, - 'index': False, - 'orders': [], - 'primary_key': False, - 'type': None, - 'unique': False, + "check": False, + "columns": [], + "foreign_key": None, + "index": False, + "orders": [], + "primary_key": False, + "type": None, + "unique": False, } - constraints[index_name]['columns'].append(column_name) - constraints[index_name]['index'] = True - constraints[index_name]['orders'].append(ordering) + constraints[index_name]["columns"].append(column_name) + constraints[index_name]["index"] = True + constraints[index_name]["orders"].append(ordering) # Index_type for PRIMARY KEY is 'PRIMARY_KEY' and NOT 'PRIMARY KEY' - is_primary_key = index_type == 'PRIMARY_KEY' - constraints[index_name]['primary_key'] = is_primary_key - constraints[index_name]['type'] = index_type if is_primary_key else Index.suffix - constraints[index_name]['unique'] = is_unique + is_primary_key = index_type == "PRIMARY_KEY" + constraints[index_name]["primary_key"] = is_primary_key + constraints[index_name]["type"] = ( + index_type if is_primary_key else Index.suffix + ) + constraints[index_name]["unique"] = is_unique return constraints diff --git a/django_spanner/lookups.py b/google/cloud/spanner_django/lookups.py similarity index 76% rename from django_spanner/lookups.py rename to google/cloud/spanner_django/lookups.py index d99f812ce1..6cd44e487d 100644 --- a/django_spanner/lookups.py +++ b/google/cloud/spanner_django/lookups.py @@ -6,8 +6,19 @@ from django.db.models import DecimalField from django.db.models.lookups import ( - Contains, EndsWith, Exact, GreaterThan, GreaterThanOrEqual, IContains, - IEndsWith, IExact, IRegex, IStartsWith, LessThan, LessThanOrEqual, Regex, + Contains, + EndsWith, + Exact, + GreaterThan, + GreaterThanOrEqual, + IContains, + IEndsWith, + IExact, + IRegex, + IStartsWith, + LessThan, + LessThanOrEqual, + Regex, StartsWith, ) @@ -17,7 +28,7 @@ def contains(self, compiler, connection): lhs_sql, params = self.process_lhs(compiler, connection) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) - is_icontains = self.lookup_name.startswith('i') + is_icontains = self.lookup_name.startswith("i") if self.rhs_is_direct_value() and params and not self.bilateral_transforms: rhs_sql = self.get_rhs_op(connection, rhs_sql) # Chop the leading and trailing percent signs that Django adds to the @@ -25,7 +36,7 @@ def contains(self, compiler, connection): params[0] = params[0][1:-1] # Add the case insensitive flag for icontains. if is_icontains: - params[0] = '(?i)' + params[0] + params[0] = "(?i)" + params[0] # rhs_sql is REGEXP_CONTAINS(%s, %%s), and lhs_sql is the column name. return rhs_sql % lhs_sql, params else: @@ -33,7 +44,11 @@ def contains(self, compiler, connection): # expression. if is_icontains: rhs_sql = "CONCAT('(?i)', " + rhs_sql + ")" - return 'REGEXP_CONTAINS(%s, %s)' % (lhs_sql, connection.pattern_esc.format(rhs_sql)), params + return ( + "REGEXP_CONTAINS(%s, %s)" + % (lhs_sql, connection.pattern_esc.format(rhs_sql)), + params, + ) def iexact(self, compiler, connection): @@ -43,12 +58,12 @@ def iexact(self, compiler, connection): rhs_sql = self.get_rhs_op(connection, rhs_sql) # Wrap the parameter in ^ and $ to restrict the regex to an exact match. if self.rhs_is_direct_value() and params and not self.bilateral_transforms: - params[0] = '^(?i)%s$' % params[0] + params[0] = "^(?i)%s$" % params[0] else: # lhs_sql is the expression/column to use as the regular expression. # Use concat to make the value case-insensitive. lhs_sql = "CONCAT('^(?i)', " + lhs_sql + ", '$')" - rhs_sql = rhs_sql.replace('%%s', '%s') + rhs_sql = rhs_sql.replace("%%s", "%s") # rhs_sql is REGEXP_CONTAINS(%s, %%s), and lhs_sql is the column name. return rhs_sql % lhs_sql, params @@ -58,11 +73,11 @@ def regex(self, compiler, connection): lhs_sql, params = self.process_lhs(compiler, connection) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) - is_iregex = self.lookup_name.startswith('i') + is_iregex = self.lookup_name.startswith("i") if self.rhs_is_direct_value() and params and not self.bilateral_transforms: rhs_sql = self.get_rhs_op(connection, rhs_sql) if is_iregex: - params[0] = '(?i)%s' % params[0] + params[0] = "(?i)%s" % params[0] else: params[0] = str(params[0]) # rhs_sql is REGEXP_CONTAINS(%s, %%s), and lhs_sql is the column name. @@ -72,7 +87,7 @@ def regex(self, compiler, connection): # expression. if is_iregex: rhs_sql = "CONCAT('(?i)', " + rhs_sql + ")" - return 'REGEXP_CONTAINS(%s, %s)' % (lhs_sql, rhs_sql), params + return "REGEXP_CONTAINS(%s, %s)" % (lhs_sql, rhs_sql), params def startswith_endswith(self, compiler, connection): @@ -80,20 +95,20 @@ def startswith_endswith(self, compiler, connection): lhs_sql, params = self.process_lhs(compiler, connection) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) - is_startswith = 'startswith' in self.lookup_name - is_endswith = 'endswith' in self.lookup_name - is_insensitive = self.lookup_name.startswith('i') + is_startswith = "startswith" in self.lookup_name + is_endswith = "endswith" in self.lookup_name + is_insensitive = self.lookup_name.startswith("i") # Chop the leading (endswith) or trailing (startswith) percent sign that # Django adds to the param since this isn't a LIKE query as Django expects. if self.rhs_is_direct_value() and params and not self.bilateral_transforms: rhs_sql = self.get_rhs_op(connection, rhs_sql) if is_endswith: - params[0] = str(params[0][1:]) + '$' + params[0] = str(params[0][1:]) + "$" else: - params[0] = '^' + str(params[0][:-1]) + params[0] = "^" + str(params[0][:-1]) # Add the case insensitive flag for istartswith or iendswith. if is_insensitive: - params[0] = '(?i)' + params[0] + params[0] = "(?i)" + params[0] # rhs_sql is REGEXP_CONTAINS(%s, %%s), and lhs_sql is the column name. return rhs_sql % lhs_sql, params else: @@ -101,14 +116,17 @@ def startswith_endswith(self, compiler, connection): # expression. sql = "CONCAT('" if is_startswith: - sql += '^' + sql += "^" if is_insensitive: - sql += '(?i)' + sql += "(?i)" sql += "', " + rhs_sql if is_endswith: sql += ", '$'" sql += ")" - return 'REGEXP_CONTAINS(%s, %s)' % (lhs_sql, connection.pattern_esc.format(sql)), params + return ( + "REGEXP_CONTAINS(%s, %s)" % (lhs_sql, connection.pattern_esc.format(sql)), + params, + ) def cast_param_to_float(self, compiler, connection): @@ -120,9 +138,13 @@ def cast_param_to_float(self, compiler, connection): if isinstance(self.lhs.output_field, DecimalField): params[0] = float(params[0]) # Cast remote field lookups that must be integer but come in as string. - elif hasattr(self.lhs.output_field, 'get_path_info'): - for i, field in enumerate(self.lhs.output_field.get_path_info()[-1].target_fields): - if field.rel_db_type(connection) == 'INT64' and isinstance(params[i], str): + elif hasattr(self.lhs.output_field, "get_path_info"): + for i, field in enumerate( + self.lhs.output_field.get_path_info()[-1].target_fields + ): + if field.rel_db_type(connection) == "INT64" and isinstance( + params[i], str + ): params[i] = int(params[i]) return sql, params diff --git a/django_spanner/operations.py b/google/cloud/spanner_django/operations.py similarity index 71% rename from django_spanner/operations.py rename to google/cloud/spanner_django/operations.py index 73be03e76e..9309bccd71 100644 --- a/django_spanner/operations.py +++ b/google/cloud/spanner_django/operations.py @@ -16,24 +16,17 @@ from django.db.utils import DatabaseError from django.utils import timezone from django.utils.duration import duration_microseconds -from spanner_dbapi.parse_utils import DateStr, TimestampStr, escape_name +from google.cloud.spanner_dbapi.parse_utils import DateStr, TimestampStr, escape_name class DatabaseOperations(BaseDatabaseOperations): - cast_data_types = { - 'CharField': 'STRING', - 'TextField': 'STRING', - } - cast_char_field_without_max_length = 'STRING' - compiler_module = 'django_spanner.compiler' + cast_data_types = {"CharField": "STRING", "TextField": "STRING"} + cast_char_field_without_max_length = "STRING" + compiler_module = "spanner_django.compiler" # Django's lookup names that require a different name in Spanner's # EXTRACT() function. # https://cloud.google.com/spanner/docs/functions-and-operators#extract - extract_names = { - 'iso_year': 'isoyear', - 'week': 'isoweek', - 'week_day': 'dayofweek', - } + extract_names = {"iso_year": "isoyear", "week": "isoweek", "week_day": "dayofweek"} def max_name_length(self): # https://cloud.google.com/spanner/quotas#tables @@ -46,8 +39,8 @@ def quote_name(self, name): # Django tests to prevent crashes. (Don't modify names in normal # operation to prevent the possibility of colliding with another # column.) https://github.com/orijtech/spanner-orm/issues/204 - if os.environ.get('RUNNING_SPANNER_BACKEND_TESTS') == '1': - name = name.replace(' ', '_').replace('-', '_') + if os.environ.get("RUNNING_SPANNER_BACKEND_TESTS") == "1": + name = name.replace(" ", "_").replace("-", "_") return escape_name(name) def bulk_batch_size(self, fields, objs): @@ -62,13 +55,12 @@ def sql_flush(self, style, tables, sequences, allow_cascade=False): # Cloud Spanner doesn't support TRUNCATE so DELETE instead. # A dummy WHERE clause is required. if tables: - delete_sql = '%s %s %%s' % ( - style.SQL_KEYWORD('DELETE'), - style.SQL_KEYWORD('FROM'), + delete_sql = "%s %s %%s" % ( + style.SQL_KEYWORD("DELETE"), + style.SQL_KEYWORD("FROM"), ) return [ - delete_sql % style.SQL_FIELD(self.quote_name(table)) - for table in tables + delete_sql % style.SQL_FIELD(self.quote_name(table)) for table in tables ] else: return [] @@ -82,7 +74,7 @@ def adapt_datetimefield_value(self, value): if value is None: return None # Expression values are adapted by the database. - if hasattr(value, 'resolve_expression'): + if hasattr(value, "resolve_expression"): return value # Cloud Spanner doesn't support tz-aware datetimes if timezone.is_aware(value): @@ -93,7 +85,7 @@ def adapt_datetimefield_value(self, value): "The Cloud Spanner backend does not support " "timezone-aware datetimes when USE_TZ is False." ) - return TimestampStr(value.isoformat(timespec='microseconds') + 'Z') + return TimestampStr(value.isoformat(timespec="microseconds") + "Z") def adapt_decimalfield_value(self, value, max_digits=None, decimal_places=None): """ @@ -108,23 +100,25 @@ def adapt_timefield_value(self, value): if value is None: return None # Expression values are adapted by the database. - if hasattr(value, 'resolve_expression'): + if hasattr(value, "resolve_expression"): return value # Column is TIMESTAMP, so prefix a dummy date to the datetime.time. - return TimestampStr('0001-01-01T' + value.isoformat(timespec='microseconds') + 'Z') + return TimestampStr( + "0001-01-01T" + value.isoformat(timespec="microseconds") + "Z" + ) def get_db_converters(self, expression): converters = super().get_db_converters(expression) internal_type = expression.output_field.get_internal_type() - if internal_type == 'DateTimeField': + if internal_type == "DateTimeField": converters.append(self.convert_datetimefield_value) - elif internal_type == 'DecimalField': + elif internal_type == "DecimalField": converters.append(self.convert_decimalfield_value) - elif internal_type == 'TimeField': + elif internal_type == "TimeField": converters.append(self.convert_timefield_value) - elif internal_type == 'BinaryField': + elif internal_type == "BinaryField": converters.append(self.convert_binaryfield_value) - elif internal_type == 'UUIDField': + elif internal_type == "UUIDField": converters.append(self.convert_uuidfield_value) return converters @@ -143,10 +137,17 @@ def convert_datetimefield_value(self, value, expression, connection): # connection's timezone). Django doesn't support nanoseconds so that # part is ignored. dt = datetime( - value.year, value.month, value.day, - value.hour, value.minute, value.second, value.microsecond, + value.year, + value.month, + value.day, + value.hour, + value.minute, + value.second, + value.microsecond, + ) + return ( + timezone.make_aware(dt, self.connection.timezone) if settings.USE_TZ else dt ) - return timezone.make_aware(dt, self.connection.timezone) if settings.USE_TZ else dt def convert_decimalfield_value(self, value, expression, connection): if value is None: @@ -167,12 +168,16 @@ def convert_uuidfield_value(self, value, expression, connection): def date_extract_sql(self, lookup_type, field_name): lookup_type = self.extract_names.get(lookup_type, lookup_type) - return 'EXTRACT(%s FROM %s)' % (lookup_type, field_name) + return "EXTRACT(%s FROM %s)" % (lookup_type, field_name) def datetime_extract_sql(self, lookup_type, field_name, tzname): - tzname = tzname if settings.USE_TZ else 'UTC' + tzname = tzname if settings.USE_TZ else "UTC" lookup_type = self.extract_names.get(lookup_type, lookup_type) - return 'EXTRACT(%s FROM %s AT TIME ZONE "%s")' % (lookup_type, field_name, tzname) + return 'EXTRACT(%s FROM %s AT TIME ZONE "%s")' % ( + lookup_type, + field_name, + tzname, + ) def time_extract_sql(self, lookup_type, field_name): # Time is stored as TIMESTAMP with UTC time zone. @@ -180,29 +185,29 @@ def time_extract_sql(self, lookup_type, field_name): def date_trunc_sql(self, lookup_type, field_name): # https://cloud.google.com/spanner/docs/functions-and-operators#date_trunc - if lookup_type == 'week': + if lookup_type == "week": # Spanner truncates to Sunday but Django expects Monday. First, # subtract a day so that a Sunday will be truncated to the previous # week... - field_name = 'DATE_SUB(CAST(' + field_name + ' AS DATE), INTERVAL 1 DAY)' - sql = 'DATE_TRUNC(CAST(%s AS DATE), %s)' % (field_name, lookup_type) - if lookup_type == 'week': + field_name = "DATE_SUB(CAST(" + field_name + " AS DATE), INTERVAL 1 DAY)" + sql = "DATE_TRUNC(CAST(%s AS DATE), %s)" % (field_name, lookup_type) + if lookup_type == "week": # ...then add a day to get from Sunday to Monday. - sql = 'DATE_ADD(CAST(' + sql + ' AS DATE), INTERVAL 1 DAY)' + sql = "DATE_ADD(CAST(" + sql + " AS DATE), INTERVAL 1 DAY)" return sql def datetime_trunc_sql(self, lookup_type, field_name, tzname): # https://cloud.google.com/spanner/docs/functions-and-operators#timestamp_trunc - tzname = tzname if settings.USE_TZ else 'UTC' - if lookup_type == 'week': + tzname = tzname if settings.USE_TZ else "UTC" + if lookup_type == "week": # Spanner truncates to Sunday but Django expects Monday. First, # subtract a day so that a Sunday will be truncated to the previous # week... - field_name = 'TIMESTAMP_SUB(' + field_name + ', INTERVAL 1 DAY)' + field_name = "TIMESTAMP_SUB(" + field_name + ", INTERVAL 1 DAY)" sql = 'TIMESTAMP_TRUNC(%s, %s, "%s")' % (field_name, lookup_type, tzname) - if lookup_type == 'week': + if lookup_type == "week": # ...then add a day to get from Sunday to Monday. - sql = 'TIMESTAMP_ADD(' + sql + ', INTERVAL 1 DAY)' + sql = "TIMESTAMP_ADD(" + sql + ", INTERVAL 1 DAY)" return sql def time_trunc_sql(self, lookup_type, field_name): @@ -211,50 +216,65 @@ def time_trunc_sql(self, lookup_type, field_name): def datetime_cast_date_sql(self, field_name, tzname): # https://cloud.google.com/spanner/docs/functions-and-operators#date - tzname = tzname if settings.USE_TZ else 'UTC' + tzname = tzname if settings.USE_TZ else "UTC" return 'DATE(%s, "%s")' % (field_name, tzname) def datetime_cast_time_sql(self, field_name, tzname): - tzname = tzname if settings.USE_TZ else 'UTC' + tzname = tzname if settings.USE_TZ else "UTC" # Cloud Spanner doesn't have a function for converting # TIMESTAMP to another time zone. - return "TIMESTAMP(FORMAT_TIMESTAMP('%%Y-%%m-%%d %%R:%%E9S %%Z', %s, '%s'))" % (field_name, tzname) + return "TIMESTAMP(FORMAT_TIMESTAMP('%%Y-%%m-%%d %%R:%%E9S %%Z', %s, '%s'))" % ( + field_name, + tzname, + ) def date_interval_sql(self, timedelta): - return 'INTERVAL %s MICROSECOND' % duration_microseconds(timedelta) + return "INTERVAL %s MICROSECOND" % duration_microseconds(timedelta) def format_for_duration_arithmetic(self, sql): - return 'INTERVAL %s MICROSECOND' % sql + return "INTERVAL %s MICROSECOND" % sql def combine_expression(self, connector, sub_expressions): - if connector == '%%': - return 'MOD(%s)' % ', '.join(sub_expressions) - elif connector == '^': - return 'POWER(%s)' % ', '.join(sub_expressions) - elif connector == '>>': + if connector == "%%": + return "MOD(%s)" % ", ".join(sub_expressions) + elif connector == "^": + return "POWER(%s)" % ", ".join(sub_expressions) + elif connector == ">>": lhs, rhs = sub_expressions # Use an alternate computation because Cloud Sapnner's '>>' operator does not do # sign bit extension with a signed type (i.e. produces different results for # negative numbers than what Django's tests expect). Cast float result as INT64 to # allow assigning to both INT64 and FLOAT64 columns (otherwise the FLOAT result # couldn't be assigned to INT64 columns). - return 'CAST(FLOOR(%(lhs)s / POW(2, %(rhs)s)) AS INT64)' % {'lhs': lhs, 'rhs': rhs} + return "CAST(FLOOR(%(lhs)s / POW(2, %(rhs)s)) AS INT64)" % { + "lhs": lhs, + "rhs": rhs, + } return super().combine_expression(connector, sub_expressions) def combine_duration_expression(self, connector, sub_expressions): - if connector == '+': - return 'TIMESTAMP_ADD(' + ', '.join(sub_expressions) + ')' - elif connector == '-': - return 'TIMESTAMP_SUB(' + ', '.join(sub_expressions) + ')' + if connector == "+": + return "TIMESTAMP_ADD(" + ", ".join(sub_expressions) + ")" + elif connector == "-": + return "TIMESTAMP_SUB(" + ", ".join(sub_expressions) + ")" else: - raise DatabaseError('Invalid connector for timedelta: %s.' % connector) + raise DatabaseError("Invalid connector for timedelta: %s." % connector) def lookup_cast(self, lookup_type, internal_type=None): # Cast text lookups to string to allow things like filter(x__contains=4) - if lookup_type in ('contains', 'icontains', 'startswith', 'istartswith', - 'endswith', 'iendswith', 'regex', 'iregex', 'iexact'): - return 'CAST(%s AS STRING)' - return '%s' + if lookup_type in ( + "contains", + "icontains", + "startswith", + "istartswith", + "endswith", + "iendswith", + "regex", + "iregex", + "iexact", + ): + return "CAST(%s AS STRING)" + return "%s" def prep_for_like_query(self, x): """Lookups that use this method use REGEXP_CONTAINS instead of LIKE.""" diff --git a/django_spanner/schema.py b/google/cloud/spanner_django/schema.py similarity index 75% rename from django_spanner/schema.py rename to google/cloud/spanner_django/schema.py index c0a113f99c..aa62d0cc86 100644 --- a/django_spanner/schema.py +++ b/google/cloud/spanner_django/schema.py @@ -9,13 +9,19 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): - sql_create_table = "CREATE TABLE %(table)s (%(definition)s) PRIMARY KEY(%(primary_key)s)" + sql_create_table = ( + "CREATE TABLE %(table)s (%(definition)s) PRIMARY KEY(%(primary_key)s)" + ) sql_delete_table = "DROP TABLE %(table)s" sql_create_fk = None # Spanner doesn't support partial indexes. This string omits the # %(condition)s placeholder so that partial indexes are ignored. - sql_create_index = "CREATE INDEX %(name)s ON %(table)s%(using)s (%(columns)s)%(extra)s" - sql_create_unique = "CREATE UNIQUE NULL_FILTERED INDEX %(name)s ON %(table)s (%(columns)s)" + sql_create_index = ( + "CREATE INDEX %(name)s ON %(table)s%(using)s (%(columns)s)%(extra)s" + ) + sql_create_unique = ( + "CREATE UNIQUE NULL_FILTERED INDEX %(name)s ON %(table)s (%(columns)s)" + ) sql_delete_unique = "DROP INDEX %(name)s" # Cloud Spanner requires when changing if a column is NULLABLE, @@ -42,7 +48,7 @@ def create_model(self, model): continue # Check constraints can go on the column SQL here db_params = field.db_parameters(connection=self.connection) - if db_params['check']: + if db_params["check"]: definition += " " + self.sql_check_constraint % db_params # Autoincrement SQL (for backends with inline variant) col_type_suffix = field.db_type_suffix(connection=self.connection) @@ -52,19 +58,22 @@ def create_model(self, model): # FK if field.remote_field and field.db_constraint: to_table = field.remote_field.model._meta.db_table - to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column + to_column = field.remote_field.model._meta.get_field( + field.remote_field.field_name + ).column if self.sql_create_inline_fk: definition += " " + self.sql_create_inline_fk % { "to_table": self.quote_name(to_table), "to_column": self.quote_name(to_column), } elif self.connection.features.supports_foreign_keys: - self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s")) + self.deferred_sql.append( + self._create_fk_sql( + model, field, "_fk_%(to_table)s_%(to_column)s" + ) + ) # Add the SQL to our big list - column_sqls.append("%s %s" % ( - self.quote_name(field.column), - definition, - )) + column_sqls.append("%s %s" % (self.quote_name(field.column), definition)) # Create a unique constraint separately because Spanner doesn't # allow them inline on a column. if field.unique and not field.primary_key: @@ -75,17 +84,24 @@ def create_model(self, model): for fields in model._meta.unique_together: columns = [model._meta.get_field(field).column for field in fields] self.deferred_sql.append(self._create_unique_sql(model, columns)) - constraints = [constraint.constraint_sql(model, self) for constraint in model._meta.constraints] + constraints = [ + constraint.constraint_sql(model, self) + for constraint in model._meta.constraints + ] # Make the table sql = self.sql_create_table % { "table": self.quote_name(model._meta.db_table), - "definition": ", ".join(constraint for constraint in (*column_sqls, *constraints) if constraint), + "definition": ", ".join( + constraint for constraint in (*column_sqls, *constraints) if constraint + ), "primary_key": self.quote_name(model._meta.pk.column), } if model._meta.db_tablespace: - tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) + tablespace_sql = self.connection.ops.tablespace_sql( + model._meta.db_tablespace + ) if tablespace_sql: - sql += ' ' + tablespace_sql + sql += " " + tablespace_sql # Prevent using [] as params, in the case a literal '%' is used in the definition self.execute(sql, params or None) @@ -116,7 +132,7 @@ def add_field(self, model, field): return # Check constraints can go on the column SQL here db_params = field.db_parameters(connection=self.connection) - if db_params['check']: + if db_params["check"]: definition += " " + self.sql_check_constraint % db_params # Build the SQL and run it sql = self.sql_create_column % { @@ -129,19 +145,27 @@ def add_field(self, model, field): # defaults for this but Spanner doesn't support them.) effective_default = self.effective_default(field) if effective_default is not None: - self.execute('UPDATE %(table)s SET %(column)s=%%s' % { - "table": self.quote_name(model._meta.db_table), - "column": self.quote_name(field.column), - }, (effective_default,)) + self.execute( + "UPDATE %(table)s SET %(column)s=%%s" + % { + "table": self.quote_name(model._meta.db_table), + "column": self.quote_name(field.column), + }, + (effective_default,), + ) # Spanner doesn't support adding NOT NULL columns to existing tables. if not field.null: - self.execute(self.sql_alter_column % { - "table": self.quote_name(model._meta.db_table), - "changes": self.sql_alter_column_not_null % { - 'column': self.quote_name(field.column), - 'type': db_params['type'], - }, - }) + self.execute( + self.sql_alter_column + % { + "table": self.quote_name(model._meta.db_table), + "changes": self.sql_alter_column_not_null + % { + "column": self.quote_name(field.column), + "type": db_params["type"], + }, + } + ) # Add an index, if required self.deferred_sql.extend(self._field_indexes_sql(model, field)) # Create a unique constraint separately because Spanner doesn't allow @@ -149,8 +173,14 @@ def add_field(self, model, field): if field.unique and not field.primary_key: self.deferred_sql.append(self._create_unique_sql(model, [field.column])) # Add any FK constraints later - if field.remote_field and self.connection.features.supports_foreign_keys and field.db_constraint: - self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s")) + if ( + field.remote_field + and self.connection.features.supports_foreign_keys + and field.db_constraint + ): + self.deferred_sql.append( + self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s") + ) def remove_field(self, model, field): # Spanner requires dropping a column's indexes before dropping the @@ -167,7 +197,7 @@ def column_sql(self, model, field, include_default=False, exclude_not_null=False """ # Get the column's type and use that as the basis of the SQL db_params = field.db_parameters(connection=self.connection) - sql = db_params['type'] + sql = db_params["type"] params = [] # Check for fields that aren't actually columns (e.g. M2M) if sql is None: @@ -176,14 +206,21 @@ def column_sql(self, model, field, include_default=False, exclude_not_null=False null = field.null # Oracle treats the empty string ('') as null, so coerce the null # option whenever '' is a possible value. - if (field.empty_strings_allowed and not field.primary_key and - self.connection.features.interprets_empty_strings_as_nulls): + if ( + field.empty_strings_allowed + and not field.primary_key + and self.connection.features.interprets_empty_strings_as_nulls + ): null = True if not null and not exclude_not_null: sql += " NOT NULL" # Optionally add the tablespace if it's an implicitly indexed column tablespace = field.db_tablespace or model._meta.db_tablespace - if tablespace and self.connection.features.supports_tablespaces and field.unique: + if ( + tablespace + and self.connection.features.supports_tablespaces + and field.unique + ): sql += " %s" % self.connection.ops.tablespace_sql(tablespace, inline=True) # Return the sql return sql, params @@ -193,7 +230,7 @@ def add_index(self, model, index): # DESC: https://code.djangoproject.com/ticket/30961 # This method can be removed in Django 3.1. index.fields_orders = [ - (field_name, ' DESC' if order == 'DESC' else '') + (field_name, " DESC" if order == "DESC" else "") for field_name, order in index.fields_orders ] super().add_index(model, index) @@ -202,15 +239,22 @@ def quote_value(self, value): # A more complete implementation isn't currently required. return str(value) - def _alter_field(self, model, old_field, new_field, old_type, new_type, - old_db_params, new_db_params, strict=False): + def _alter_field( + self, + model, + old_field, + new_field, + old_type, + new_type, + old_db_params, + new_db_params, + strict=False, + ): # Spanner requires dropping indexes before changing the nullability # of a column. nullability_changed = old_field.null != new_field.null if nullability_changed: - index_names = self._constraint_names( - model, [old_field.column], index=True, - ) + index_names = self._constraint_names(model, [old_field.column], index=True) if index_names and not old_field.db_index: raise NotSupportedError( "Changing nullability of a field with an index other than " @@ -224,8 +268,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, for index_name in index_names: self.execute(self._delete_index_sql(model, index_name)) super()._alter_field( - model, old_field, new_field, old_type, new_type, - old_db_params, new_db_params, strict=False, + model, + old_field, + new_field, + old_type, + new_type, + old_db_params, + new_db_params, + strict=False, ) # Recreate the index that was dropped earlier. if nullability_changed and new_field.db_index: @@ -234,15 +284,13 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, def _alter_column_type_sql(self, model, old_field, new_field, new_type): # Spanner needs to use sql_alter_column_not_null if the field is # NOT NULL, otherwise the constraint is dropped. - sql = self.sql_alter_column_type if new_field.null else self.sql_alter_column_not_null + sql = ( + self.sql_alter_column_type + if new_field.null + else self.sql_alter_column_not_null + ) return ( - ( - sql % { - "column": self.quote_name(new_field.column), - "type": new_type, - }, - [], - ), + (sql % {"column": self.quote_name(new_field.column), "type": new_type}, []), [], ) diff --git a/django_spanner/utils.py b/google/cloud/spanner_django/utils.py similarity index 67% rename from django_spanner/utils.py rename to google/cloud/spanner_django/utils.py index e1e642fa34..1136c33a87 100644 --- a/django_spanner/utils.py +++ b/google/cloud/spanner_django/utils.py @@ -10,12 +10,11 @@ def check_django_compatability(): with Django 2.2.y. """ from . import __version__ + if django.VERSION[:2] != get_version_tuple(__version__)[:2]: raise ImproperlyConfigured( - 'You must use the latest version of django-spanner {A}.{B}.x ' - 'with Django {A}.{B}.y (found django-spanner {C}).'.format( - A=django.VERSION[0], - B=django.VERSION[1], - C=__version__, + "You must use the latest version of django-spanner {A}.{B}.x " + "with Django {A}.{B}.y (found django-spanner {C}).".format( + A=django.VERSION[0], B=django.VERSION[1], C=__version__ ) ) diff --git a/django_spanner/validation.py b/google/cloud/spanner_django/validation.py similarity index 67% rename from django_spanner/validation.py rename to google/cloud/spanner_django/validation.py index d34fe2382b..f8c21b802a 100644 --- a/django_spanner/validation.py +++ b/google/cloud/spanner_django/validation.py @@ -6,16 +6,17 @@ class DatabaseValidation(BaseDatabaseValidation): - def check_field_type(self, field, field_type): errors = [] # Disable the error when running the Django test suite. - if os.environ.get('RUNNING_SPANNER_BACKEND_TESTS') != '1' and isinstance(field, DecimalField): + if os.environ.get("RUNNING_SPANNER_BACKEND_TESTS") != "1" and isinstance( + field, DecimalField + ): errors.append( checks.Error( - 'DecimalField is not yet supported by Spanner.', + "DecimalField is not yet supported by Spanner.", obj=field, - id='spanner.E001', + id="spanner.E001", ) ) return errors diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000..d39a7af1be --- /dev/null +++ b/noxfile.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import absolute_import +import os +import shutil + +import nox + + +BLACK_VERSION = "black==19.3b0" +BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] + +if os.path.exists("samples"): + BLACK_PATHS.append("samples") + + +@nox.session(python="3.7") +def lint(session): + """Run linters. + + Returns a failure if the linters find linting errors or sufficiently + serious code quality issues. + """ + session.install("flake8", BLACK_VERSION) + session.run("black", "--check", *BLACK_PATHS) + session.run("flake8", "google", "tests") + + +@nox.session(python="3.6") +def blacken(session): + """Run black. + + Format code to uniform standard. + + This currently uses Python 3.6 due to the automated Kokoro run of synthtool. + That run uses an image that doesn't have 3.6 installed. Before updating this + check the state of the `gcp_ubuntu_config` we use for that Kokoro run. + """ + session.install(BLACK_VERSION) + session.run("black", *BLACK_PATHS) + + +@nox.session(python="3.7") +def lint_setup_py(session): + """Verify that setup.py is valid (including RST check).""" + session.install("docutils", "pygments") + session.run("python", "setup.py", "check", "--restructuredtext", "--strict") + + +def default(session): + # Install all test dependencies, then install this package in-place. + session.install("mock", "pytest", "pytest-cov") + session.install("-e", ".") + + # Run py.test against the unit tests. + session.run( + "py.test", + "--quiet", + "--cov=google.cloud", + "--cov=tests.unit", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", + "--cov-fail-under=0", + os.path.join("tests", "unit"), + *session.posargs, + ) + + +@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) +def unit(session): + """Run the unit test suite.""" + default(session) + + +@nox.session(python=["2.7", "3.7"]) +def system(session): + """Run the system test suite.""" + system_test_path = os.path.join("tests", "system.py") + system_test_folder_path = os.path.join("tests", "system") + # Sanity check: Only run tests if either credentials or emulator host is set. + if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", "") and not os.environ.get( + "SPANNER_EMULATOR_HOST", "" + ): + session.skip( + "Credentials or emulator host must be set via environment variable" + ) + + system_test_exists = os.path.exists(system_test_path) + system_test_folder_exists = os.path.exists(system_test_folder_path) + # Sanity check: only run tests if found. + if not system_test_exists and not system_test_folder_exists: + session.skip("System tests were not found") + + # Use pre-release gRPC for system tests. + session.install("--pre", "grpcio") + + # Install all test dependencies, then install this package into the + # virtualenv's dist-packages. + session.install("mock", "pytest") + + session.install("-e", ".") + session.install("-e", "test_utils/") + + # Run py.test against the system tests. + if system_test_exists: + session.run("py.test", "--quiet", system_test_path, *session.posargs) + if system_test_folder_exists: + session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) + + +@nox.session(python="3.7") +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.install("coverage", "pytest-cov") + session.run("coverage", "report", "--show-missing", "--fail-under=99") + + session.run("coverage", "erase") + + +@nox.session(python="3.7") +def docs(session): + """Build the docs for this library.""" + + session.install("-e", ".") + session.install("sphinx", "alabaster", "recommonmark") + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-W", # warnings as errors + "-T", # show full traceback on exception + "-N", # no colors + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) diff --git a/runtests.py b/runtests.py index c0ae9c3121..ee903a3b0f 100644 --- a/runtests.py +++ b/runtests.py @@ -23,7 +23,7 @@ def run_django_tests(): def main(): - run_unittests('tests.spanner_dbapi') + run_unittests('tests.unit') if __name__ == '__main__': diff --git a/examples/from-scratch/README.md b/samples/from-scratch/README.md similarity index 96% rename from examples/from-scratch/README.md rename to samples/from-scratch/README.md index f82a1e4b86..e10b8e7b59 100644 --- a/examples/from-scratch/README.md +++ b/samples/from-scratch/README.md @@ -31,7 +31,7 @@ Once in, please edit the file `hc/local_settings.py`, and: a) add `django_spanner` as the very first entry to your `INSTALLED_APPS` ```python INSTALLED_APPS = [ - 'django_spanner', # Must be listed first. + 'spanner_django', # Must be listed first. ... ] ``` @@ -40,7 +40,7 @@ b) update `DATABASES` into the following: ```python DATABASES = { 'default': { - 'ENGINE': 'django_spanner', + 'ENGINE': 'spanner_django', 'PROJECT': PROJECT_ID, 'INSTANCE': SPANNER_INSTANCE, 'NAME': SPANNER_DATABASE_NAME, @@ -59,7 +59,7 @@ which when filled out, will look like this ```python DATABASES = { 'default': { - 'ENGINE': 'django_spanner', + 'ENGINE': 'spanner_django', 'PROJECT': 'spanner-appdev', 'INSTANCE': 'instance', 'NAME': 'healthchecks_db', diff --git a/examples/healthchecks/README.md b/samples/healthchecks/README.md similarity index 99% rename from examples/healthchecks/README.md rename to samples/healthchecks/README.md index f28ed4e845..7f51db463a 100644 --- a/examples/healthchecks/README.md +++ b/samples/healthchecks/README.md @@ -65,7 +65,7 @@ Once in, please edit the file `hc/local_settings.py` to: a) Add `django_spanner` as the first entry to `INSTALLED_APPS` ```python INSTALLED_APPS = [ - 'django_spanner', # Must be listed first. + 'spanner_django', # Must be listed first. ... ] ``` @@ -76,7 +76,7 @@ b) Edit `DATABASES` into the following: ```python DATABASES = { 'default': { - 'ENGINE': 'django_spanner', + 'ENGINE': 'spanner_django', 'PROJECT': PROJECT_ID, 'INSTANCE': SPANNER_INSTANCE, 'NAME': SPANNER_DATABASE_NAME, @@ -95,7 +95,7 @@ which when filled out, will look like this ```python DATABASES = { 'default': { - 'ENGINE': 'django_spanner', + 'ENGINE': 'spanner_django', 'PROJECT': 'spanner-appdev', 'INSTANCE': 'instance', 'NAME': 'healthchecks_db', diff --git a/setup.py b/setup.py index e82752e4c4..c3971217d0 100644 --- a/setup.py +++ b/setup.py @@ -6,12 +6,11 @@ import io import os - -from setuptools import find_packages, setup +import setuptools # Package metadata. -name = "django-google-spanner" +name = "google-cloud-spanner-django" description = "Bridge to enable using Django with Spanner." version = "2.2a0" # Should be one of: @@ -20,8 +19,10 @@ # 'Development Status :: 5 - Production/Stable' release_status = "Development Status :: 3 - Alpha" dependencies = [ - 'sqlparse >= 0.3.0', - 'google-cloud-spanner >= 1.8.0', + "sqlparse >= 0.3.0", + "google-api-core[grpc, grpcgcp] >= 1.14.0, < 2.0.0dev", + "google-cloud-core >= 1.0.3, < 2.0dev", + "google-cloud-spanner >= 1.8.0", ] extras = {} @@ -30,36 +31,52 @@ package_root = os.path.abspath(os.path.dirname(__file__)) -readme_filename = os.path.join(package_root, "README.md") +readme_filename = os.path.join(package_root, "README.rst") with io.open(readme_filename, encoding="utf-8") as readme_file: readme = readme_file.read() +# Only include packages under the 'google' namespace. Do not include tests, +# benchmarks, etc. +packages = [ + package for package in setuptools.find_packages() if package.startswith("google") +] + +# Determine which namespaces are needed. +namespaces = ["google"] +if "google.cloud" in packages: + namespaces.append("google.cloud") + -setup( - name=name, - version=version, - description=description, - long_description=readme, - author='Google LLC', - author_email='cloud-spanner-developers@googlegroups.com', - license='BSD', - packages=find_packages(exclude=['tests']), - install_requires=dependencies, - url="https://github.com/googleapis/python-spanner-django", - classifiers=[ - release_status, - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Utilities', - 'Framework :: Django', - 'Framework :: Django :: 2.2', - ], - extras_require=extras, - python_requires=">=3.5", +setuptools.setup( + name=name, + version=version, + description=description, + long_description=readme, + author="Google LLC", + author_email="cloud-spanner-developers@googlegroups.com", + license="BSD", + url="https://github.com/googleapis/python-spanner-django", + classifiers=[ + release_status, + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Utilities", + "Framework :: Django", + "Framework :: Django :: 2.2", + ], + platforms="Posix; MacOS X; Windows", + packages=packages, + namespace_packages=namespaces, + install_requires=dependencies, + extras_require=extras, + # python_requires=">=3.5", + python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + include_package_data=True, + zip_safe=False, ) diff --git a/tests/spanner_dbapi/test_globals.py b/tests/spanner_dbapi/test_globals.py deleted file mode 100644 index b1f6280115..0000000000 --- a/tests/spanner_dbapi/test_globals.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -from unittest import TestCase - -from spanner_dbapi import apilevel, paramstyle, threadsafety - - -class DBAPIGlobalsTests(TestCase): - def test_apilevel(self): - self.assertEqual(apilevel, '2.0', 'We implement PEP-0249 version 2.0') - self.assertEqual(paramstyle, 'format', 'Cloud Spanner uses @param') - self.assertEqual(threadsafety, 1, 'Threads may share module but not connections') diff --git a/tests/spanner_dbapi/test_parse_utils.py b/tests/spanner_dbapi/test_parse_utils.py deleted file mode 100644 index f0da0145c7..0000000000 --- a/tests/spanner_dbapi/test_parse_utils.py +++ /dev/null @@ -1,427 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -import datetime -import decimal -from unittest import TestCase - -from google.cloud.spanner_v1 import param_types -from spanner_dbapi.exceptions import Error, ProgrammingError -from spanner_dbapi.parse_utils import ( - STMT_DDL, STMT_NON_UPDATING, DateStr, TimestampStr, classify_stmt, - ensure_where_clause, escape_name, get_param_types, parse_insert, - rows_for_insert_or_update, sql_pyformat_args_to_spanner, strip_backticks, -) -from spanner_dbapi.utils import backtick_unicode - - -class ParseUtilsTests(TestCase): - def test_classify_stmt(self): - cases = [ - ('SELECT 1', STMT_NON_UPDATING,), - ('SELECT s.SongName FROM Songs AS s', STMT_NON_UPDATING,), - ('WITH sq AS (SELECT SchoolID FROM Roster) SELECT * from sq', STMT_NON_UPDATING,), - ( - 'CREATE TABLE django_content_type (id STRING(64) NOT NULL, name STRING(100) ' - 'NOT NULL, app_label STRING(100) NOT NULL, model STRING(100) NOT NULL) PRIMARY KEY(id)', - STMT_DDL, - ), - ( - 'CREATE INDEX SongsBySingerAlbumSongNameDesc ON ' - 'Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums', - STMT_DDL, - ), - ('CREATE INDEX SongsBySongName ON Songs(SongName)', STMT_DDL,), - ('CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)', STMT_DDL,), - ] - - for tt in cases: - sql, want_classification = tt - got_classification = classify_stmt(sql) - self.assertEqual(got_classification, want_classification, 'Classification mismatch') - - def test_parse_insert(self): - cases = [ - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - [1, 2, 3, 4, 5, 6], - { - 'sql_params_list': [ - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - (1, 2, 3,), - ), - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - (4, 5, 6,), - ), - ], - }, - ), - ( - 'INSERT INTO django_migrations(app, name, applied) VALUES (%s, %s, %s)', - [1, 2, 3, 4, 5, 6], - { - 'sql_params_list': [ - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - (1, 2, 3,), - ), - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)', - (4, 5, 6,), - ), - ], - }, - ), - ( - 'INSERT INTO sales.addresses (street, city, state, zip_code) ' - 'SELECT street, city, state, zip_code FROM sales.customers' - 'ORDER BY first_name, last_name', - None, - { - 'sql_params_list': [( - 'INSERT INTO sales.addresses (street, city, state, zip_code) ' - 'SELECT street, city, state, zip_code FROM sales.customers' - 'ORDER BY first_name, last_name', - None, - )], - } - ), - ( - - 'INSERT INTO ap (n, ct, cn) ' - 'VALUES (%s, %s, %s), (%s, %s, %s), (%s, %s, %s),(%s, %s, %s)', - (1, 2, 3, 4, 5, 6, 7, 8, 9), - { - 'sql_params_list': [ - ( - 'INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)', - (1, 2, 3,), - ), - ( - 'INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)', - (4, 5, 6,), - ), - ( - 'INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)', - (7, 8, 9,), - ), - ], - }, - ), - ( - 'INSERT INTO `no` (`yes`) VALUES (%s)', - (1, 4, 5), - { - 'sql_params_list': [ - ( - 'INSERT INTO `no` (`yes`) VALUES (%s)', - (1,), - ), - ( - 'INSERT INTO `no` (`yes`) VALUES (%s)', - (4,), - ), - ( - 'INSERT INTO `no` (`yes`) VALUES (%s)', - (5,), - ), - ], - }, - ), - ( - 'INSERT INTO T (f1, f2) VALUES (1, 2)', - None, - { - 'sql_params_list': [ - ( - 'INSERT INTO T (f1, f2) VALUES (1, 2)', - None, - ), - ], - }, - ), - ( - 'INSERT INTO `no` (`yes`, tiff) VALUES (%s, LOWER(%s)), (%s, %s), (%s, %s)', - (1, 'FOO', 5, 10, 11, 29), - { - 'sql_params_list': [ - ('INSERT INTO `no` (`yes`, tiff) VALUES(%s, LOWER(%s))', (1, 'FOO',)), - ('INSERT INTO `no` (`yes`, tiff) VALUES(%s, %s)', (5, 10)), - ('INSERT INTO `no` (`yes`, tiff) VALUES(%s, %s)', (11, 29)), - ], - }, - ), - ] - - for sql, params, want in cases: - with self.subTest(sql=sql): - got = parse_insert(sql, params) - self.assertEqual(got, want, 'Mismatch with parse_insert of `%s`' % sql) - - def test_parse_insert_invalid(self): - cases = [ - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, %s)', - [1, 2, 3, 4, 5, 6, 7], - 'len\\(params\\)=7 MUST be a multiple of len\\(pyformat_args\\)=3', - ), - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, LOWER(%s))', - [1, 2, 3, 4, 5, 6, 7], - 'Invalid length: VALUES\\(...\\) len: 6 != len\\(params\\): 7', - ), - ( - 'INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, LOWER(%s)))', - [1, 2, 3, 4, 5, 6], - 'VALUES: expected `,` got \\) in \\)', - ), - ] - - for sql, params, wantException in cases: - with self.subTest(sql=sql): - self.assertRaisesRegex( - ProgrammingError, - wantException, - lambda: parse_insert(sql, params), - ) - - def test_rows_for_insert_or_update(self): - cases = [ - ( - ['id', 'app', 'name'], - [(5, 'ap', 'n',), (6, 'bp', 'm',)], - None, - [(5, 'ap', 'n',), (6, 'bp', 'm',)], - ), - ( - ['app', 'name'], - [('ap', 'n',), ('bp', 'm',)], - None, - [('ap', 'n'), ('bp', 'm',)] - ), - ( - ['app', 'name', 'fn'], - ['ap', 'n', 'f1', 'bp', 'm', 'f2', 'cp', 'o', 'f3'], - ['(%s, %s, %s)', '(%s, %s, %s)', '(%s, %s, %s)'], - [('ap', 'n', 'f1',), ('bp', 'm', 'f2',), ('cp', 'o', 'f3',)] - ), - ( - ['app', 'name', 'fn', 'ln'], - [('ap', 'n', (45, 'nested',), 'll',), ('bp', 'm', 'f2', 'mt',), ('fp', 'cp', 'o', 'f3',)], - None, - [ - ('ap', 'n', (45, 'nested',), 'll',), - ('bp', 'm', 'f2', 'mt',), - ('fp', 'cp', 'o', 'f3',), - ], - ), - ( - ['app', 'name', 'fn'], - ['ap', 'n', 'f1'], - None, - [('ap', 'n', 'f1',)] - ), - ] - - for i, (columns, params, pyformat_args, want) in enumerate(cases): - with self.subTest(i=i): - got = rows_for_insert_or_update(columns, params, pyformat_args) - self.assertEqual(got, want) - - def test_sql_pyformat_args_to_spanner(self): - cases = [ - ( - ('SELECT * from t WHERE f1=%s, f2 = %s, f3=%s', (10, 'abc', 'y**$22l3f',)), - ('SELECT * from t WHERE f1=@a0, f2 = @a1, f3=@a2', {'a0': 10, 'a1': 'abc', 'a2': 'y**$22l3f'}), - ), - ( - ('INSERT INTO t (f1, f2, f2) VALUES (%s, %s, %s)', ('app', 'name', 'applied',)), - ('INSERT INTO t (f1, f2, f2) VALUES (@a0, @a1, @a2)', {'a0': 'app', 'a1': 'name', 'a2': 'applied'}), - ), - ( - ( - 'INSERT INTO t (f1, f2, f2) VALUES (%(f1)s, %(f2)s, %(f3)s)', - {'f1': 'app', 'f2': 'name', 'f3': 'applied'}, - ), - ( - 'INSERT INTO t (f1, f2, f2) VALUES (@a0, @a1, @a2)', - {'a0': 'app', 'a1': 'name', 'a2': 'applied'}, - ), - ), - ( - # Intentionally using a dict with more keys than will be resolved. - ('SELECT * from t WHERE f1=%(f1)s', {'f1': 'app', 'f2': 'name'}), - ('SELECT * from t WHERE f1=@a0', {'a0': 'app'}), - ), - ( - # No args to replace, we MUST return the original params dict - # since it might be useful to pass to the next user. - ('SELECT * from t WHERE id=10', {'f1': 'app', 'f2': 'name'}), - ('SELECT * from t WHERE id=10', {'f1': 'app', 'f2': 'name'}), - ), - ( - ('SELECT (an.p + %s) AS np FROM an WHERE (an.p + %s) = %s', (1, 1.0, decimal.Decimal('31'),)), - ('SELECT (an.p + @a0) AS np FROM an WHERE (an.p + @a1) = @a2', {'a0': 1, 'a1': 1.0, 'a2': 31.0}), - ), - ] - for ((sql_in, params), sql_want) in cases: - with self.subTest(sql=sql_in): - got_sql, got_named_args = sql_pyformat_args_to_spanner(sql_in, params) - want_sql, want_named_args = sql_want - self.assertEqual(got_sql, want_sql, 'SQL does not match') - self.assertEqual(got_named_args, want_named_args, 'Named args do not match') - - def test_sql_pyformat_args_to_spanner_invalid(self): - cases = [ - ('SELECT * from t WHERE f1=%s, f2 = %s, f3=%s, extra=%s', (10, 'abc', 'y**$22l3f',)), - ] - for sql, params in cases: - with self.subTest(sql=sql): - self.assertRaisesRegex(Error, 'pyformat_args mismatch', - lambda: sql_pyformat_args_to_spanner(sql, params), - ) - - def test_get_param_types(self): - cases = [ - ( - {'a1': 10, 'b1': '2005-08-30T01:01:01.000001Z', 'c1': '2019-12-05', 'd1': 10.39}, - { - 'a1': param_types.INT64, - 'b1': param_types.STRING, - 'c1': param_types.STRING, - 'd1': param_types.FLOAT64, - }, - ), - ( - {'a1': 10, 'b1': TimestampStr('2005-08-30T01:01:01.000001Z'), 'c1': '2019-12-05'}, - {'a1': param_types.INT64, 'b1': param_types.TIMESTAMP, 'c1': param_types.STRING}, - ), - ( - {'a1': 10, 'b1': '2005-08-30T01:01:01.000001Z', 'c1': DateStr('2019-12-05')}, - {'a1': param_types.INT64, 'b1': param_types.STRING, 'c1': param_types.DATE}, - ), - ( - {'a1': 10, 'b1': '2005-08-30T01:01:01.000001Z'}, - {'a1': param_types.INT64, 'b1': param_types.STRING}, - ), - ( - {'a1': 10, 'b1': TimestampStr('2005-08-30T01:01:01.000001Z'), 'c1': DateStr('2005-08-30')}, - {'a1': param_types.INT64, 'b1': param_types.TIMESTAMP, 'c1': param_types.DATE}, - ), - ( - {'a1': 10, 'b1': 'aaaaa08-30T01:01:01.000001Z'}, - {'a1': param_types.INT64, 'b1': param_types.STRING}, - ), - ( - {'a1': 10, 'b1': '2005-08-30T01:01:01.000001', 't1': True, 't2': False, 'f1': 99e9}, - { - 'a1': param_types.INT64, - 'b1': param_types.STRING, - 't1': param_types.BOOL, - 't2': param_types.BOOL, - 'f1': param_types.FLOAT64, - }, - ), - ( - {'a1': 10, 'b1': '2019-11-26T02:55:41.000000Z'}, - {'a1': param_types.INT64, 'b1': param_types.STRING}, - ), - ( - { - 'a1': 10, 'b1': TimestampStr('2019-11-26T02:55:41.000000Z'), - 'dt1': datetime.datetime(2011, 9, 1, 13, 20, 30), - 'd1': datetime.date(2011, 9, 1), - }, - { - 'a1': param_types.INT64, 'b1': param_types.TIMESTAMP, - 'dt1': param_types.TIMESTAMP, 'd1': param_types.DATE, - }, - ), - ( - {'a1': 10, 'b1': TimestampStr('2019-11-26T02:55:41.000000Z')}, - {'a1': param_types.INT64, 'b1': param_types.TIMESTAMP}, - ), - ({'a1': b'bytes'}, {'a1': param_types.BYTES}), - ({'a1': 10, 'b1': None}, {'a1': param_types.INT64}), - (None, None), - ] - - for i, (params, want_param_types) in enumerate(cases): - with self.subTest(i=i): - got_param_types = get_param_types(params) - self.assertEqual(got_param_types, want_param_types) - - def test_ensure_where_clause(self): - cases = [ - ( - 'UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1', - 'UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1', - ), - ( - 'UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5', - 'UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5 WHERE 1=1', - ), - ( - 'UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2', - 'UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2', - ), - ( - 'UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)', - 'UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)', - ), - ( - 'UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)', - 'UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)', - ), - ( - 'DELETE * FROM TABLE', - 'DELETE * FROM TABLE WHERE 1=1', - ), - ] - - for sql, want in cases: - with self.subTest(sql=sql): - got = ensure_where_clause(sql) - self.assertEqual(got, want) - - def test_escape_name(self): - cases = [ - ('SELECT', '`SELECT`'), - ('id', 'id'), - ('', ''), - ('dashed-value', '`dashed-value`'), - ('with space', '`with space`'), - ] - - for name, want in cases: - with self.subTest(name=name): - got = escape_name(name) - self.assertEqual(got, want) - - def test_strip_backticks(self): - cases = [ - ('foo', 'foo'), - ('`foo`', 'foo'), - ] - for name, want in cases: - with self.subTest(name=name): - got = strip_backticks(name) - self.assertEqual(got, want) - - def test_backtick_unicode(self): - cases = [ - ('SELECT (1) as foo WHERE 1=1', 'SELECT (1) as foo WHERE 1=1'), - ('SELECT (1) as föö', 'SELECT (1) as `föö`'), - ('SELECT (1) as `föö`', 'SELECT (1) as `föö`'), - ('SELECT (1) as `föö` `umläut', 'SELECT (1) as `föö` `umläut'), - ('SELECT (1) as `föö', 'SELECT (1) as `föö'), - ] - for sql, want in cases: - with self.subTest(sql=sql): - got = backtick_unicode(sql) - self.assertEqual(got, want) diff --git a/tests/spanner_dbapi/test_parser.py b/tests/spanner_dbapi/test_parser.py deleted file mode 100644 index 3d288bf0fb..0000000000 --- a/tests/spanner_dbapi/test_parser.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2020 Google LLC -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file or at -# https://developers.google.com/open-source/licenses/bsd - -from unittest import TestCase - -from spanner_dbapi.exceptions import ProgrammingError -from spanner_dbapi.parser import ( - ARGS, FUNC, TERMINAL, VALUES, a_args, expect, func, pyfmt_str, values, -) - - -class ParserTests(TestCase): - def test_terminal(self): - cases = [ - ('%s', '', pyfmt_str), - (' %s', '', pyfmt_str), - (' %s ', '', pyfmt_str), - ] - - for text, want_unconsumed, want_parsed in cases: - with self.subTest(text=text): - got_unconsumed, got_parsed = expect(text, TERMINAL) - self.assertEqual(got_parsed, want_parsed) - self.assertEqual(got_unconsumed, want_unconsumed) - - def test_terminal_fail(self): - cases = [ - ('', 'TERMINAL: `` is not %s'), - ('fdp', 'TERMINAL: `fdp` is not %s'), - ('%%s', 'TERMINAL: `%%s` is not %s'), - ('%sa', 'TERMINAL: `%sa` is not %s'), - ] - - for text, wantException in cases: - with self.subTest(text=text): - self.assertRaisesRegex(ProgrammingError, wantException, lambda: expect(text, TERMINAL)) - - def test_func(self): - cases = [ - ('_91())', ')', func('_91', a_args([]))), - ('_a()', '', func('_a', a_args([]))), - ('___()', '', func('___', a_args([]))), - ('abc()', '', func('abc', a_args([]))), - ( - 'AF112(%s, LOWER(%s, %s), rand(%s, %s, TAN(%s, %s)))', - '', - func('AF112', a_args([ - pyfmt_str, - func('LOWER', a_args([pyfmt_str, pyfmt_str])), - func('rand', a_args([ - pyfmt_str, pyfmt_str, func('TAN', a_args([pyfmt_str, pyfmt_str])), - ])), - ])), - ), - ] - - for text, want_unconsumed, want_parsed in cases: - with self.subTest(text=text): - got_unconsumed, got_parsed = expect(text, FUNC) - self.assertEqual(got_parsed, want_parsed) - self.assertEqual(got_unconsumed, want_unconsumed) - - def test_func_fail(self): - cases = [ - ('', 'FUNC: `` does not begin with `a-zA-z` nor a `_`'), - ('91', 'FUNC: `91` does not begin with `a-zA-z` nor a `_`'), - ('_91', 'supposed to begin with `\\(`'), - ('_91(', 'supposed to end with `\\)`'), - ('_.()', 'supposed to begin with `\\(`'), - ('_a.b()', 'supposed to begin with `\\(`'), - ] - - for text, wantException in cases: - with self.subTest(text=text): - self.assertRaisesRegex(ProgrammingError, wantException, lambda: expect(text, FUNC)) - - def test_a_args(self): - cases = [ - ('()', '', a_args([])), - ('(%s)', '', a_args([pyfmt_str])), - ('(%s,)', '', a_args([pyfmt_str])), - ('(%s),', ',', a_args([pyfmt_str])), - ( - '(%s,%s, f1(%s, %s))', '', a_args([ - pyfmt_str, pyfmt_str, func('f1', a_args([pyfmt_str, pyfmt_str])), - ]), - ), - ] - - for text, want_unconsumed, want_parsed in cases: - with self.subTest(text=text): - got_unconsumed, got_parsed = expect(text, ARGS) - self.assertEqual(got_parsed, want_parsed) - self.assertEqual(got_unconsumed, want_unconsumed) - - def test_a_args_fail(self): - cases = [ - ('', 'ARGS: supposed to begin with `\\(`'), - ('(', 'ARGS: supposed to end with `\\)`'), - (')', 'ARGS: supposed to begin with `\\(`'), - ('(%s,%s, f1(%s, %s), %s', 'ARGS: supposed to end with `\\)`'), - ] - - for text, wantException in cases: - with self.subTest(text=text): - self.assertRaisesRegex(ProgrammingError, wantException, lambda: expect(text, ARGS)) - - def test_expect_values(self): - cases = [ - ('VALUES ()', '', values([a_args([])])), - ('VALUES', '', values([])), - ('VALUES(%s)', '', values([a_args([pyfmt_str])])), - (' VALUES (%s) ', '', values([a_args([pyfmt_str])])), - ('VALUES(%s, %s)', '', values([a_args([pyfmt_str, pyfmt_str])])), - ( - 'VALUES(%s, %s, LOWER(%s, %s))', '', - values([ - a_args([ - pyfmt_str, - pyfmt_str, - func('LOWER', a_args([pyfmt_str, pyfmt_str])), - ]), - ]), - ), - ( - 'VALUES (UPPER(%s)), (%s)', - '', - values([ - a_args([ - func('UPPER', a_args([pyfmt_str])), - ]), - a_args([ - pyfmt_str, - ]), - ]), - ), - ] - - for text, want_unconsumed, want_parsed in cases: - with self.subTest(text=text): - got_unconsumed, got_parsed = expect(text, VALUES) - self.assertEqual(got_parsed, want_parsed) - self.assertEqual(got_unconsumed, want_unconsumed) - - def test_expect_values_fail(self): - cases = [ - ('', 'VALUES: `` does not start with VALUES'), - ( - 'VALUES(%s, %s, (%s, %s))', - 'FUNC: `\\(%s, %s\\)\\)` does not begin with `a-zA-z` nor a `_`', - ), - ('VALUES(%s),,', 'ARGS: supposed to begin with `\\(` in `,`'), - ] - - for text, wantException in cases: - with self.subTest(text=text): - self.assertRaisesRegex(ProgrammingError, wantException, lambda: expect(text, VALUES)) diff --git a/tests/spanner_dbapi/__init__.py b/tests/unit/__init__.py similarity index 100% rename from tests/spanner_dbapi/__init__.py rename to tests/unit/__init__.py diff --git a/tests/unit/test_globals.py b/tests/unit/test_globals.py new file mode 100644 index 0000000000..7c3e0396a9 --- /dev/null +++ b/tests/unit/test_globals.py @@ -0,0 +1,18 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +from unittest import TestCase + +from google.cloud.spanner_dbapi import apilevel, paramstyle, threadsafety + + +class DBAPIGlobalsTests(TestCase): + def test_apilevel(self): + self.assertEqual(apilevel, "2.0", "We implement PEP-0249 version 2.0") + self.assertEqual(paramstyle, "format", "Cloud Spanner uses @param") + self.assertEqual( + threadsafety, 1, "Threads may share module but not connections" + ) diff --git a/tests/unit/test_parse_utils.py b/tests/unit/test_parse_utils.py new file mode 100644 index 0000000000..0a86ac091b --- /dev/null +++ b/tests/unit/test_parse_utils.py @@ -0,0 +1,477 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +import datetime +import decimal +from unittest import TestCase + +from google.cloud.spanner_v1 import param_types +from google.cloud.spanner_dbapi.exceptions import Error, ProgrammingError +from google.cloud.spanner_dbapi.parse_utils import ( + STMT_DDL, + STMT_NON_UPDATING, + DateStr, + TimestampStr, + classify_stmt, + ensure_where_clause, + escape_name, + get_param_types, + parse_insert, + rows_for_insert_or_update, + sql_pyformat_args_to_spanner, + strip_backticks, +) +from google.cloud.spanner_dbapi.utils import backtick_unicode + + +class ParseUtilsTests(TestCase): + def test_classify_stmt(self): + cases = [ + ("SELECT 1", STMT_NON_UPDATING), + ("SELECT s.SongName FROM Songs AS s", STMT_NON_UPDATING), + ( + "WITH sq AS (SELECT SchoolID FROM Roster) SELECT * from sq", + STMT_NON_UPDATING, + ), + ( + "CREATE TABLE django_content_type (id STRING(64) NOT NULL, name STRING(100) " + "NOT NULL, app_label STRING(100) NOT NULL, model STRING(100) NOT NULL) PRIMARY KEY(id)", + STMT_DDL, + ), + ( + "CREATE INDEX SongsBySingerAlbumSongNameDesc ON " + "Songs(SingerId, AlbumId, SongName DESC), INTERLEAVE IN Albums", + STMT_DDL, + ), + ("CREATE INDEX SongsBySongName ON Songs(SongName)", STMT_DDL), + ( + "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)", + STMT_DDL, + ), + ] + + for tt in cases: + sql, want_classification = tt + got_classification = classify_stmt(sql) + self.assertEqual( + got_classification, want_classification, "Classification mismatch" + ) + + def test_parse_insert(self): + cases = [ + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)", + [1, 2, 3, 4, 5, 6], + { + "sql_params_list": [ + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)", + (1, 2, 3), + ), + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)", + (4, 5, 6), + ), + ] + }, + ), + ( + "INSERT INTO django_migrations(app, name, applied) VALUES (%s, %s, %s)", + [1, 2, 3, 4, 5, 6], + { + "sql_params_list": [ + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)", + (1, 2, 3), + ), + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s)", + (4, 5, 6), + ), + ] + }, + ), + ( + "INSERT INTO sales.addresses (street, city, state, zip_code) " + "SELECT street, city, state, zip_code FROM sales.customers" + "ORDER BY first_name, last_name", + None, + { + "sql_params_list": [ + ( + "INSERT INTO sales.addresses (street, city, state, zip_code) " + "SELECT street, city, state, zip_code FROM sales.customers" + "ORDER BY first_name, last_name", + None, + ) + ] + }, + ), + ( + "INSERT INTO ap (n, ct, cn) " + "VALUES (%s, %s, %s), (%s, %s, %s), (%s, %s, %s),(%s, %s, %s)", + (1, 2, 3, 4, 5, 6, 7, 8, 9), + { + "sql_params_list": [ + ("INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)", (1, 2, 3)), + ("INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)", (4, 5, 6)), + ("INSERT INTO ap (n, ct, cn) VALUES (%s, %s, %s)", (7, 8, 9)), + ] + }, + ), + ( + "INSERT INTO `no` (`yes`) VALUES (%s)", + (1, 4, 5), + { + "sql_params_list": [ + ("INSERT INTO `no` (`yes`) VALUES (%s)", (1,)), + ("INSERT INTO `no` (`yes`) VALUES (%s)", (4,)), + ("INSERT INTO `no` (`yes`) VALUES (%s)", (5,)), + ] + }, + ), + ( + "INSERT INTO T (f1, f2) VALUES (1, 2)", + None, + {"sql_params_list": [("INSERT INTO T (f1, f2) VALUES (1, 2)", None)]}, + ), + ( + "INSERT INTO `no` (`yes`, tiff) VALUES (%s, LOWER(%s)), (%s, %s), (%s, %s)", + (1, "FOO", 5, 10, 11, 29), + { + "sql_params_list": [ + ( + "INSERT INTO `no` (`yes`, tiff) VALUES(%s, LOWER(%s))", + (1, "FOO"), + ), + ("INSERT INTO `no` (`yes`, tiff) VALUES(%s, %s)", (5, 10)), + ("INSERT INTO `no` (`yes`, tiff) VALUES(%s, %s)", (11, 29)), + ] + }, + ), + ] + + for sql, params, want in cases: + with self.subTest(sql=sql): + got = parse_insert(sql, params) + self.assertEqual(got, want, "Mismatch with parse_insert of `%s`" % sql) + + def test_parse_insert_invalid(self): + cases = [ + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, %s)", + [1, 2, 3, 4, 5, 6, 7], + "len\\(params\\)=7 MUST be a multiple of len\\(pyformat_args\\)=3", + ), + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, LOWER(%s))", + [1, 2, 3, 4, 5, 6, 7], + "Invalid length: VALUES\\(...\\) len: 6 != len\\(params\\): 7", + ), + ( + "INSERT INTO django_migrations (app, name, applied) VALUES (%s, %s, %s), (%s, %s, LOWER(%s)))", + [1, 2, 3, 4, 5, 6], + "VALUES: expected `,` got \\) in \\)", + ), + ] + + for sql, params, wantException in cases: + with self.subTest(sql=sql): + self.assertRaisesRegex( + ProgrammingError, wantException, lambda: parse_insert(sql, params) + ) + + def test_rows_for_insert_or_update(self): + cases = [ + ( + ["id", "app", "name"], + [(5, "ap", "n"), (6, "bp", "m")], + None, + [(5, "ap", "n"), (6, "bp", "m")], + ), + ( + ["app", "name"], + [("ap", "n"), ("bp", "m")], + None, + [("ap", "n"), ("bp", "m")], + ), + ( + ["app", "name", "fn"], + ["ap", "n", "f1", "bp", "m", "f2", "cp", "o", "f3"], + ["(%s, %s, %s)", "(%s, %s, %s)", "(%s, %s, %s)"], + [("ap", "n", "f1"), ("bp", "m", "f2"), ("cp", "o", "f3")], + ), + ( + ["app", "name", "fn", "ln"], + [ + ("ap", "n", (45, "nested"), "ll"), + ("bp", "m", "f2", "mt"), + ("fp", "cp", "o", "f3"), + ], + None, + [ + ("ap", "n", (45, "nested"), "ll"), + ("bp", "m", "f2", "mt"), + ("fp", "cp", "o", "f3"), + ], + ), + (["app", "name", "fn"], ["ap", "n", "f1"], None, [("ap", "n", "f1")]), + ] + + for i, (columns, params, pyformat_args, want) in enumerate(cases): + with self.subTest(i=i): + got = rows_for_insert_or_update(columns, params, pyformat_args) + self.assertEqual(got, want) + + def test_sql_pyformat_args_to_spanner(self): + cases = [ + ( + ( + "SELECT * from t WHERE f1=%s, f2 = %s, f3=%s", + (10, "abc", "y**$22l3f"), + ), + ( + "SELECT * from t WHERE f1=@a0, f2 = @a1, f3=@a2", + {"a0": 10, "a1": "abc", "a2": "y**$22l3f"}, + ), + ), + ( + ( + "INSERT INTO t (f1, f2, f2) VALUES (%s, %s, %s)", + ("app", "name", "applied"), + ), + ( + "INSERT INTO t (f1, f2, f2) VALUES (@a0, @a1, @a2)", + {"a0": "app", "a1": "name", "a2": "applied"}, + ), + ), + ( + ( + "INSERT INTO t (f1, f2, f2) VALUES (%(f1)s, %(f2)s, %(f3)s)", + {"f1": "app", "f2": "name", "f3": "applied"}, + ), + ( + "INSERT INTO t (f1, f2, f2) VALUES (@a0, @a1, @a2)", + {"a0": "app", "a1": "name", "a2": "applied"}, + ), + ), + ( + # Intentionally using a dict with more keys than will be resolved. + ("SELECT * from t WHERE f1=%(f1)s", {"f1": "app", "f2": "name"}), + ("SELECT * from t WHERE f1=@a0", {"a0": "app"}), + ), + ( + # No args to replace, we MUST return the original params dict + # since it might be useful to pass to the next user. + ("SELECT * from t WHERE id=10", {"f1": "app", "f2": "name"}), + ("SELECT * from t WHERE id=10", {"f1": "app", "f2": "name"}), + ), + ( + ( + "SELECT (an.p + %s) AS np FROM an WHERE (an.p + %s) = %s", + (1, 1.0, decimal.Decimal("31")), + ), + ( + "SELECT (an.p + @a0) AS np FROM an WHERE (an.p + @a1) = @a2", + {"a0": 1, "a1": 1.0, "a2": 31.0}, + ), + ), + ] + for ((sql_in, params), sql_want) in cases: + with self.subTest(sql=sql_in): + got_sql, got_named_args = sql_pyformat_args_to_spanner(sql_in, params) + want_sql, want_named_args = sql_want + self.assertEqual(got_sql, want_sql, "SQL does not match") + self.assertEqual( + got_named_args, want_named_args, "Named args do not match" + ) + + def test_sql_pyformat_args_to_spanner_invalid(self): + cases = [ + ( + "SELECT * from t WHERE f1=%s, f2 = %s, f3=%s, extra=%s", + (10, "abc", "y**$22l3f"), + ) + ] + for sql, params in cases: + with self.subTest(sql=sql): + self.assertRaisesRegex( + Error, + "pyformat_args mismatch", + lambda: sql_pyformat_args_to_spanner(sql, params), + ) + + def test_get_param_types(self): + cases = [ + ( + { + "a1": 10, + "b1": "2005-08-30T01:01:01.000001Z", + "c1": "2019-12-05", + "d1": 10.39, + }, + { + "a1": param_types.INT64, + "b1": param_types.STRING, + "c1": param_types.STRING, + "d1": param_types.FLOAT64, + }, + ), + ( + { + "a1": 10, + "b1": TimestampStr("2005-08-30T01:01:01.000001Z"), + "c1": "2019-12-05", + }, + { + "a1": param_types.INT64, + "b1": param_types.TIMESTAMP, + "c1": param_types.STRING, + }, + ), + ( + { + "a1": 10, + "b1": "2005-08-30T01:01:01.000001Z", + "c1": DateStr("2019-12-05"), + }, + { + "a1": param_types.INT64, + "b1": param_types.STRING, + "c1": param_types.DATE, + }, + ), + ( + {"a1": 10, "b1": "2005-08-30T01:01:01.000001Z"}, + {"a1": param_types.INT64, "b1": param_types.STRING}, + ), + ( + { + "a1": 10, + "b1": TimestampStr("2005-08-30T01:01:01.000001Z"), + "c1": DateStr("2005-08-30"), + }, + { + "a1": param_types.INT64, + "b1": param_types.TIMESTAMP, + "c1": param_types.DATE, + }, + ), + ( + {"a1": 10, "b1": "aaaaa08-30T01:01:01.000001Z"}, + {"a1": param_types.INT64, "b1": param_types.STRING}, + ), + ( + { + "a1": 10, + "b1": "2005-08-30T01:01:01.000001", + "t1": True, + "t2": False, + "f1": 99e9, + }, + { + "a1": param_types.INT64, + "b1": param_types.STRING, + "t1": param_types.BOOL, + "t2": param_types.BOOL, + "f1": param_types.FLOAT64, + }, + ), + ( + {"a1": 10, "b1": "2019-11-26T02:55:41.000000Z"}, + {"a1": param_types.INT64, "b1": param_types.STRING}, + ), + ( + { + "a1": 10, + "b1": TimestampStr("2019-11-26T02:55:41.000000Z"), + "dt1": datetime.datetime(2011, 9, 1, 13, 20, 30), + "d1": datetime.date(2011, 9, 1), + }, + { + "a1": param_types.INT64, + "b1": param_types.TIMESTAMP, + "dt1": param_types.TIMESTAMP, + "d1": param_types.DATE, + }, + ), + ( + {"a1": 10, "b1": TimestampStr("2019-11-26T02:55:41.000000Z")}, + {"a1": param_types.INT64, "b1": param_types.TIMESTAMP}, + ), + ({"a1": b"bytes"}, {"a1": param_types.BYTES}), + ({"a1": 10, "b1": None}, {"a1": param_types.INT64}), + (None, None), + ] + + for i, (params, want_param_types) in enumerate(cases): + with self.subTest(i=i): + got_param_types = get_param_types(params) + self.assertEqual(got_param_types, want_param_types) + + def test_ensure_where_clause(self): + cases = [ + ( + "UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1", + "UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1", + ), + ( + "UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5", + "UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5 WHERE 1=1", + ), + ( + "UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2", + "UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2", + ), + ( + "UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)", + "UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)", + ), + ( + "UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)", + "UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)", + ), + ("DELETE * FROM TABLE", "DELETE * FROM TABLE WHERE 1=1"), + ] + + for sql, want in cases: + with self.subTest(sql=sql): + got = ensure_where_clause(sql) + self.assertEqual(got, want) + + def test_escape_name(self): + cases = [ + ("SELECT", "`SELECT`"), + ("id", "id"), + ("", ""), + ("dashed-value", "`dashed-value`"), + ("with space", "`with space`"), + ] + + for name, want in cases: + with self.subTest(name=name): + got = escape_name(name) + self.assertEqual(got, want) + + def test_strip_backticks(self): + cases = [("foo", "foo"), ("`foo`", "foo")] + for name, want in cases: + with self.subTest(name=name): + got = strip_backticks(name) + self.assertEqual(got, want) + + def test_backtick_unicode(self): + cases = [ + ("SELECT (1) as foo WHERE 1=1", "SELECT (1) as foo WHERE 1=1"), + ("SELECT (1) as föö", "SELECT (1) as `föö`"), + ("SELECT (1) as `föö`", "SELECT (1) as `föö`"), + ("SELECT (1) as `föö` `umläut", "SELECT (1) as `föö` `umläut"), + ("SELECT (1) as `föö", "SELECT (1) as `föö"), + ] + for sql, want in cases: + with self.subTest(sql=sql): + got = backtick_unicode(sql) + self.assertEqual(got, want) diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py new file mode 100644 index 0000000000..daffc5a8ae --- /dev/null +++ b/tests/unit/test_parser.py @@ -0,0 +1,190 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +from unittest import TestCase + +from google.cloud.spanner_dbapi.exceptions import ProgrammingError +from google.cloud.spanner_dbapi.parser import ( + ARGS, + FUNC, + TERMINAL, + VALUES, + a_args, + expect, + func, + pyfmt_str, + values, +) + + +class ParserTests(TestCase): + def test_terminal(self): + cases = [ + ("%s", "", pyfmt_str), + (" %s", "", pyfmt_str), + (" %s ", "", pyfmt_str), + ] + + for text, want_unconsumed, want_parsed in cases: + with self.subTest(text=text): + got_unconsumed, got_parsed = expect(text, TERMINAL) + self.assertEqual(got_parsed, want_parsed) + self.assertEqual(got_unconsumed, want_unconsumed) + + def test_terminal_fail(self): + cases = [ + ("", "TERMINAL: `` is not %s"), + ("fdp", "TERMINAL: `fdp` is not %s"), + ("%%s", "TERMINAL: `%%s` is not %s"), + ("%sa", "TERMINAL: `%sa` is not %s"), + ] + + for text, wantException in cases: + with self.subTest(text=text): + self.assertRaisesRegex( + ProgrammingError, wantException, lambda: expect(text, TERMINAL) + ) + + def test_func(self): + cases = [ + ("_91())", ")", func("_91", a_args([]))), + ("_a()", "", func("_a", a_args([]))), + ("___()", "", func("___", a_args([]))), + ("abc()", "", func("abc", a_args([]))), + ( + "AF112(%s, LOWER(%s, %s), rand(%s, %s, TAN(%s, %s)))", + "", + func( + "AF112", + a_args( + [ + pyfmt_str, + func("LOWER", a_args([pyfmt_str, pyfmt_str])), + func( + "rand", + a_args( + [ + pyfmt_str, + pyfmt_str, + func("TAN", a_args([pyfmt_str, pyfmt_str])), + ] + ), + ), + ] + ), + ), + ), + ] + + for text, want_unconsumed, want_parsed in cases: + with self.subTest(text=text): + got_unconsumed, got_parsed = expect(text, FUNC) + self.assertEqual(got_parsed, want_parsed) + self.assertEqual(got_unconsumed, want_unconsumed) + + def test_func_fail(self): + cases = [ + ("", "FUNC: `` does not begin with `a-zA-z` nor a `_`"), + ("91", "FUNC: `91` does not begin with `a-zA-z` nor a `_`"), + ("_91", "supposed to begin with `\\(`"), + ("_91(", "supposed to end with `\\)`"), + ("_.()", "supposed to begin with `\\(`"), + ("_a.b()", "supposed to begin with `\\(`"), + ] + + for text, wantException in cases: + with self.subTest(text=text): + self.assertRaisesRegex( + ProgrammingError, wantException, lambda: expect(text, FUNC) + ) + + def test_a_args(self): + cases = [ + ("()", "", a_args([])), + ("(%s)", "", a_args([pyfmt_str])), + ("(%s,)", "", a_args([pyfmt_str])), + ("(%s),", ",", a_args([pyfmt_str])), + ( + "(%s,%s, f1(%s, %s))", + "", + a_args( + [pyfmt_str, pyfmt_str, func("f1", a_args([pyfmt_str, pyfmt_str]))] + ), + ), + ] + + for text, want_unconsumed, want_parsed in cases: + with self.subTest(text=text): + got_unconsumed, got_parsed = expect(text, ARGS) + self.assertEqual(got_parsed, want_parsed) + self.assertEqual(got_unconsumed, want_unconsumed) + + def test_a_args_fail(self): + cases = [ + ("", "ARGS: supposed to begin with `\\(`"), + ("(", "ARGS: supposed to end with `\\)`"), + (")", "ARGS: supposed to begin with `\\(`"), + ("(%s,%s, f1(%s, %s), %s", "ARGS: supposed to end with `\\)`"), + ] + + for text, wantException in cases: + with self.subTest(text=text): + self.assertRaisesRegex( + ProgrammingError, wantException, lambda: expect(text, ARGS) + ) + + def test_expect_values(self): + cases = [ + ("VALUES ()", "", values([a_args([])])), + ("VALUES", "", values([])), + ("VALUES(%s)", "", values([a_args([pyfmt_str])])), + (" VALUES (%s) ", "", values([a_args([pyfmt_str])])), + ("VALUES(%s, %s)", "", values([a_args([pyfmt_str, pyfmt_str])])), + ( + "VALUES(%s, %s, LOWER(%s, %s))", + "", + values( + [ + a_args( + [ + pyfmt_str, + pyfmt_str, + func("LOWER", a_args([pyfmt_str, pyfmt_str])), + ] + ) + ] + ), + ), + ( + "VALUES (UPPER(%s)), (%s)", + "", + values( + [a_args([func("UPPER", a_args([pyfmt_str]))]), a_args([pyfmt_str])] + ), + ), + ] + + for text, want_unconsumed, want_parsed in cases: + with self.subTest(text=text): + got_unconsumed, got_parsed = expect(text, VALUES) + self.assertEqual(got_parsed, want_parsed) + self.assertEqual(got_unconsumed, want_unconsumed) + + def test_expect_values_fail(self): + cases = [ + ("", "VALUES: `` does not start with VALUES"), + ( + "VALUES(%s, %s, (%s, %s))", + "FUNC: `\\(%s, %s\\)\\)` does not begin with `a-zA-z` nor a `_`", + ), + ("VALUES(%s),,", "ARGS: supposed to begin with `\\(` in `,`"), + ] + + for text, wantException in cases: + with self.subTest(text=text): + self.assertRaisesRegex( + ProgrammingError, wantException, lambda: expect(text, VALUES) + ) diff --git a/tests/spanner_dbapi/test_types.py b/tests/unit/test_types.py similarity index 72% rename from tests/spanner_dbapi/test_types.py rename to tests/unit/test_types.py index 6d79cfba08..afbd03bc3b 100644 --- a/tests/spanner_dbapi/test_types.py +++ b/tests/unit/test_types.py @@ -7,10 +7,15 @@ import datetime from unittest import TestCase -from spanner_dbapi.types import ( - Date, DateFromTicks, Time, TimeFromTicks, Timestamp, TimestampFromTicks, +from google.cloud.spanner_dbapi.types import ( + Date, + DateFromTicks, + Time, + TimeFromTicks, + Timestamp, + TimestampFromTicks, ) -from spanner_dbapi.utils import PeekIterator +from google.cloud.spanner_dbapi.utils import PeekIterator tzUTC = 0 # 0 hours offset from UTC @@ -19,17 +24,17 @@ class TypesTests(TestCase): def test_Date(self): got = Date(2019, 11, 3) want = datetime.date(2019, 11, 3) - self.assertEqual(got, want, 'mismatch between conversion') + self.assertEqual(got, want, "mismatch between conversion") def test_Time(self): got = Time(23, 8, 19) want = datetime.time(23, 8, 19) - self.assertEqual(got, want, 'mismatch between conversion') + self.assertEqual(got, want, "mismatch between conversion") def test_Timestamp(self): got = Timestamp(2019, 11, 3, 23, 8, 19) want = datetime.datetime(2019, 11, 3, 23, 8, 19) - self.assertEqual(got, want, 'mismatch between conversion') + self.assertEqual(got, want, "mismatch between conversion") def test_DateFromTicks(self): epochTicks = 1572851662.9782631 # Sun Nov 03 23:14:22 2019 @@ -42,7 +47,7 @@ def test_DateFromTicks(self): datetime.datetime(2019, 11, 4, tzUTC).date(), ) matches = got in want - self.assertTrue(matches, '`%s` not present in any of\n`%s`' % (got, want)) + self.assertTrue(matches, "`%s` not present in any of\n`%s`" % (got, want)) def test_TimeFromTicks(self): epochTicks = 1572851662.9782631 # Sun Nov 03 23:14:22 2019 @@ -55,7 +60,7 @@ def test_TimeFromTicks(self): datetime.datetime(2019, 11, 4, 7, 14, 22, tzUTC).time(), ) matches = got in want - self.assertTrue(matches, '`%s` not present in any of\n`%s`' % (got, want)) + self.assertTrue(matches, "`%s` not present in any of\n`%s`" % (got, want)) def test_TimestampFromTicks(self): epochTicks = 1572851662.9782631 # Sun Nov 03 23:14:22 2019 @@ -68,15 +73,15 @@ def test_TimestampFromTicks(self): datetime.datetime(2019, 11, 4, 7, 14, 22, tzUTC), ) matches = got in want - self.assertTrue(matches, '`%s` not present in any of\n`%s`' % (got, want)) + self.assertTrue(matches, "`%s` not present in any of\n`%s`" % (got, want)) def test_PeekIterator(self): cases = [ - ('list', [1, 2, 3, 4, 6, 7], [1, 2, 3, 4, 6, 7]), - ('iter_from_list', iter([1, 2, 3, 4, 6, 7]), [1, 2, 3, 4, 6, 7]), - ('tuple', ('a', 12, 0xff,), ['a', 12, 0xff]), - ('iter_from_tuple', iter(('a', 12, 0xff,)), ['a', 12, 0xff]), - ('no_args', (), []), + ("list", [1, 2, 3, 4, 6, 7], [1, 2, 3, 4, 6, 7]), + ("iter_from_list", iter([1, 2, 3, 4, 6, 7]), [1, 2, 3, 4, 6, 7]), + ("tuple", ("a", 12, 0xFF), ["a", 12, 0xFF]), + ("iter_from_tuple", iter(("a", 12, 0xFF)), ["a", 12, 0xFF]), + ("no_args", (), []), ] for name, data_in, want in cases: diff --git a/tests/spanner_dbapi/test_utils.py b/tests/unit/test_utils.py similarity index 54% rename from tests/spanner_dbapi/test_utils.py rename to tests/unit/test_utils.py index e97a285a79..34148e0549 100644 --- a/tests/spanner_dbapi/test_utils.py +++ b/tests/unit/test_utils.py @@ -6,7 +6,7 @@ from unittest import TestCase -from spanner_dbapi.utils import PeekIterator +from google.cloud.spanner_dbapi.utils import PeekIterator class UtilsTests(TestCase): @@ -15,22 +15,22 @@ def test_peekIterator_list_rows_converted_to_tuples(self): # PeekIterator is used by BaseCursor in its fetch* methods. # This test ensures that anything passed into PeekIterator # will be returned as a tuple. - pit = PeekIterator([['a'], ['b'], ['c'], ['d'], ['e']]) + pit = PeekIterator([["a"], ["b"], ["c"], ["d"], ["e"]]) got = list(pit) - want = [('a',), ('b',), ('c',), ('d',), ('e',)] - self.assertEqual(got, want, 'Rows of type list must be returned as tuples') + want = [("a",), ("b",), ("c",), ("d",), ("e",)] + self.assertEqual(got, want, "Rows of type list must be returned as tuples") seventeen = PeekIterator([[17]]) self.assertEqual(list(seventeen), [(17,)]) - pit = PeekIterator([['%', '%d']]) - self.assertEqual(next(pit), ('%', '%d',)) + pit = PeekIterator([["%", "%d"]]) + self.assertEqual(next(pit), ("%", "%d")) - pit = PeekIterator([('Clark', 'Kent')]) - self.assertEqual(next(pit), ('Clark', 'Kent',)) + pit = PeekIterator([("Clark", "Kent")]) + self.assertEqual(next(pit), ("Clark", "Kent")) def test_peekIterator_nonlist_rows_unconverted(self): - pi = PeekIterator(['a', 'b', 'c', 'd', 'e']) + pi = PeekIterator(["a", "b", "c", "d", "e"]) got = list(pi) - want = ['a', 'b', 'c', 'd', 'e'] - self.assertEqual(got, want, 'Values should be returned unchanged') + want = ["a", "b", "c", "d", "e"] + self.assertEqual(got, want, "Values should be returned unchanged") diff --git a/tests/spanner_dbapi/test_version.py b/tests/unit/test_version.py similarity index 71% rename from tests/spanner_dbapi/test_version.py rename to tests/unit/test_version.py index 9700226c28..63ad8682f3 100644 --- a/tests/spanner_dbapi/test_version.py +++ b/tests/unit/test_version.py @@ -8,7 +8,7 @@ from unittest import TestCase from google.api_core.gapic_v1.client_info import ClientInfo -from spanner_dbapi.version import DEFAULT_USER_AGENT, google_client_info +from google.cloud.spanner_dbapi import DEFAULT_USER_AGENT, google_client_info vers = sys.version_info @@ -18,14 +18,14 @@ def test_google_client_info_default_useragent(self): got = google_client_info().to_grpc_metadata() want = ClientInfo( user_agent=DEFAULT_USER_AGENT, - python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0), + python_version="%d.%d.%d" % (vers.major, vers.minor, vers.micro or 0), ).to_grpc_metadata() self.assertEqual(got, want) def test_google_client_info_custom_useragent(self): - got = google_client_info('custom-user-agent').to_grpc_metadata() + got = google_client_info("custom-user-agent").to_grpc_metadata() want = ClientInfo( - user_agent='custom-user-agent', - python_version='%d.%d.%d' % (vers.major, vers.minor, vers.micro or 0), + user_agent="custom-user-agent", + python_version="%d.%d.%d" % (vers.major, vers.minor, vers.micro or 0), ).to_grpc_metadata() self.assertEqual(got, want)