From 437e0ba5337e7bcc1425e1506675e6e780d06f42 Mon Sep 17 00:00:00 2001
From: Shai Berger <shai@platonix.com>
Date: Sun, 29 Jan 2017 00:33:13 +0200
Subject: [PATCH] [1.11.x] Fixed #25192 -- Fixed squashmigrations crash with
 RunPython.noop on Python 2.

Thanks Adam Johnson for review.
---
 django/db/migrations/operations/special.py    |  9 +++++++++
 tests/migrations/test_commands.py             |  4 ++++
 .../0001_initial.py                           | 11 +++++++++++
 .../0002_second.py                            | 19 +++++++++++++++++++
 .../test_migrations_squash_noop/__init__.py   |  0
 5 files changed, 43 insertions(+)
 create mode 100644 tests/migrations/test_migrations_squash_noop/0001_initial.py
 create mode 100644 tests/migrations/test_migrations_squash_noop/0002_second.py
 create mode 100644 tests/migrations/test_migrations_squash_noop/__init__.py

diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py
index 66c5a3af27..9ca0b07b0b 100644
--- a/django/db/migrations/operations/special.py
+++ b/django/db/migrations/operations/special.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 from django.db import router
+from django.utils import six
 
 from .base import Operation
 
@@ -203,3 +204,11 @@ class RunPython(Operation):
     @staticmethod
     def noop(apps, schema_editor):
         return None
+
+
+# Allow migrations using RunPython.noop to be squashed on Python 2 (it doesn't
+# support serializating unbound method so install a module function instead).
+if six.PY2:
+    def noop(apps, schema_editor):
+        return None
+    RunPython.noop = staticmethod(noop)
diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py
index 6477873e0d..fa2a8a23fd 100644
--- a/tests/migrations/test_commands.py
+++ b/tests/migrations/test_commands.py
@@ -1392,3 +1392,7 @@ class SquashMigrationsTests(MigrationTestBase):
             )
             with self.assertRaisesMessage(CommandError, msg):
                 call_command("squashmigrations", "migrations", "0003", "0002", interactive=False, verbosity=0)
+
+    def test_squashmigrations_squashes_noop(self):
+        with self.temporary_migration_module(module="migrations.test_migrations_squash_noop"):
+            call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=0)
diff --git a/tests/migrations/test_migrations_squash_noop/0001_initial.py b/tests/migrations/test_migrations_squash_noop/0001_initial.py
new file mode 100644
index 0000000000..8c2e38872a
--- /dev/null
+++ b/tests/migrations/test_migrations_squash_noop/0001_initial.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    operations = []
diff --git a/tests/migrations/test_migrations_squash_noop/0002_second.py b/tests/migrations/test_migrations_squash_noop/0002_second.py
new file mode 100644
index 0000000000..f8deee10d1
--- /dev/null
+++ b/tests/migrations/test_migrations_squash_noop/0002_second.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+def dummy_python_op(apps, schema_editor):
+    return None
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("migrations", "0001_initial"),
+    ]
+
+    operations = [
+        migrations.RunPython(dummy_python_op, migrations.RunPython.noop, elidable=False),
+    ]
diff --git a/tests/migrations/test_migrations_squash_noop/__init__.py b/tests/migrations/test_migrations_squash_noop/__init__.py
new file mode 100644
index 0000000000..e69de29bb2