Repository: django-extensions/django-extensions

Issue:
runscript not compatible with apps that use AppConfig in INSTALLED_APPS
Since Django 1.7 (https://docs.djangoproject.com/en/1.7/ref/applications/#configuring-applications), it is possible to use:

``` python

INSTALLED_APPS = (
    "my_app.apps.MyAppConfig",
)
```

But runscript fails to load scripts for these apps.
It looks for `my_app.apps.MyAppConfig.scripts.my_script` instead of `my_app.scripts.my_script`.



Discussion:

Resolving patch:
diff --git a/django_extensions/compat.py b/django_extensions/compat.py
--- a/django_extensions/compat.py
+++ b/django_extensions/compat.py
@@ -3,6 +3,7 @@
 import sys
 
 import django
+from django.conf import settings
 
 # flake8: noqa
 
@@ -36,3 +37,13 @@
     User.get_username = lambda self: self.username
     def get_user_model():
         return User
+
+
+def list_apps():
+    try:
+        # django >= 1.7, to support AppConfig
+        from django.apps import apps
+        return [app.name for app in apps.get_app_configs()]
+    except ImportError:
+        # old way
+        return settings.INSTALLED_APPS
diff --git a/django_extensions/management/commands/runscript.py b/django_extensions/management/commands/runscript.py
--- a/django_extensions/management/commands/runscript.py
+++ b/django_extensions/management/commands/runscript.py
@@ -2,9 +2,7 @@
 import traceback
 from optparse import make_option
 
-from django.conf import settings
-
-from django_extensions.compat import importlib
+from django_extensions.compat import importlib, list_apps
 from django_extensions.management.email_notifications import \
     EmailNotificationCommand
 from django_extensions.management.utils import signalcommand
@@ -135,7 +133,7 @@ def find_modules_for_script(script):
             """ find script module which contains 'run' attribute """
             modules = []
             # first look in apps
-            for app in settings.INSTALLED_APPS:
+            for app in list_apps():
                 for subdir in subdirs:
                     mod = my_import("%s.%s.%s" % (app, subdir, script))
                     if mod:


Test patch:
diff --git a/tests/test_runscript.py b/tests/test_runscript.py
new file mode 100644
--- /dev/null
+++ b/tests/test_runscript.py
@@ -0,0 +1,33 @@
+from django.core.management import call_command
+from django.test import TestCase
+import django
+import pytest
+import six
+import sys
+
+
+class RunScriptTests(TestCase):
+
+    def setUp(self):
+        sys.stdout = six.StringIO()
+        sys.stderr = six.StringIO()
+
+    def test_runs(self):
+        # lame test...does it run?
+        call_command('runscript', 'sample_script', verbosity=2)
+        self.assertIn("Found script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue())
+        self.assertIn("Running script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue())
+
+    @pytest.mark.skipif(
+        django.VERSION < (1, 7),
+        reason="AppConfig and modify_settings appeared in 1.7"
+    )
+    def test_runs_appconfig(self):
+
+        with self.modify_settings(INSTALLED_APPS={
+            'append': 'tests.testapp.apps.TestAppConfig',
+            'remove': 'tests.testapp',
+        }):
+            call_command('runscript', 'sample_script', verbosity=2)
+            self.assertIn("Found script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue())
+            self.assertIn("Running script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue())
diff --git a/tests/testapp/apps.py b/tests/testapp/apps.py
new file mode 100644
--- /dev/null
+++ b/tests/testapp/apps.py
@@ -0,0 +1,8 @@
+try:
+    from django.apps import AppConfig
+
+    class TestAppConfig(AppConfig):
+        name = 'tests.testapp'
+
+except ImportError:
+    pass
diff --git a/tests/testapp/scripts/__init__.py b/tests/testapp/scripts/__init__.py
new file mode 100644
diff --git a/tests/testapp/scripts/sample_script.py b/tests/testapp/scripts/sample_script.py
new file mode 100644
--- /dev/null
+++ b/tests/testapp/scripts/sample_script.py
@@ -0,0 +1,2 @@
+def run():
+    pass