From b4273d9fd7bdb1efc215ba2b85d2236b083fe24f Mon Sep 17 00:00:00 2001 From: Jens Kleineheismann Date: Thu, 31 Oct 2019 16:57:40 +0100 Subject: [PATCH] Begin to implement #1 --- src/django_deploy/config.py | 2 +- src/django_deploy/django_settings.py | 3 + src/django_deploy/program.py | 33 +++++++-- src/django_deploy/tests/django_settings.py | 1 - src/django_deploy/tests/fake_app/__init__.py | 0 .../tests/fake_app/django_settings.py | 4 + src/django_deploy/tests/fake_app/urls.py | 2 + src/django_deploy/tests/test_program.py | 73 ++++++++++++++++--- src/django_deploy/tests/test_settings.py | 4 + src/django_deploy/tests/test_utils.py | 16 ++++ src/django_deploy/utils.py | 41 +++++++++++ 11 files changed, 161 insertions(+), 18 deletions(-) delete mode 100644 src/django_deploy/tests/django_settings.py create mode 100644 src/django_deploy/tests/fake_app/__init__.py create mode 100644 src/django_deploy/tests/fake_app/django_settings.py create mode 100644 src/django_deploy/tests/fake_app/urls.py create mode 100644 src/django_deploy/tests/test_settings.py create mode 100644 src/django_deploy/tests/test_utils.py create mode 100644 src/django_deploy/utils.py diff --git a/src/django_deploy/config.py b/src/django_deploy/config.py index 55970af..decb621 100644 --- a/src/django_deploy/config.py +++ b/src/django_deploy/config.py @@ -1 +1 @@ -DJANGO_SETTINGS_MODULE_NAME = 'main' +DJANGO_SETTINGS_DIR = 'main' diff --git a/src/django_deploy/django_settings.py b/src/django_deploy/django_settings.py index 81fcbb1..3b21247 100644 --- a/src/django_deploy/django_settings.py +++ b/src/django_deploy/django_settings.py @@ -1 +1,4 @@ # ADD_INSTALLED_APPS = ['django_deploy'] +# ADD_URLPATTERNS = [ +# {'type': 'include', 'pattern': '', 'module': 'django_deploy.urls'}, +# ] diff --git a/src/django_deploy/program.py b/src/django_deploy/program.py index efb5d9f..e1d3a92 100644 --- a/src/django_deploy/program.py +++ b/src/django_deploy/program.py @@ -4,7 +4,7 @@ import importlib import os import sys -from .config import DJANGO_SETTINGS_MODULE_NAME +from .config import DJANGO_SETTINGS_DIR from .version import VERSION @@ -38,7 +38,7 @@ class Program(object): # pylint: disable=too-few-public-methods @staticmethod def _append_to_settings(project_dir, code, comment): - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME) + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) settings_file = os.path.join(settings_dir, 'settings.py') settings_file_py2cache = settings_file + 'c' @@ -57,7 +57,7 @@ class Program(object): # pylint: disable=too-few-public-methods else: os.makedirs(project_dir) - settings_module = DJANGO_SETTINGS_MODULE_NAME + settings_module = DJANGO_SETTINGS_DIR cmd = 'django-admin startproject {name} {path}'.format(name=settings_module, path=project_dir) sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir)) @@ -65,7 +65,7 @@ class Program(object): # pylint: disable=too-few-public-methods return exitval def _add_installed_apps_from_app(self, project_dir, app): - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME) + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) settings_from_app = importlib.import_module('{}.django_settings'.format(app)) if hasattr(settings_from_app, 'ADD_INSTALLED_APPS'): @@ -102,6 +102,29 @@ class Program(object): # pylint: disable=too-few-public-methods return os.EX_OK + def _add_urlpatterns_from_app(self, project_dir, app): + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) + + settings_from_app = importlib.import_module('{}.django_settings'.format(app)) + if hasattr(settings_from_app, 'ADD_URLPATTERNS'): + add_urls = settings_from_app.ADD_URLPATTERNS + else: + add_urls = [] + + if not add_urls: + sys.stdout.write('{}: do not care about ROOT_URLCONF\n'.format(app)) + return os.EX_OK + + raise NotImplementedError() + self._append_to_settings(project_dir, code, comment) + + def _merge_app(self, project_dir, app): + exitval = self._add_installed_apps_from_app(project_dir, app) + if exitval != os.EX_OK: + return exitval + exitval = self._add_urlpatterns_from_app(project_dir, app) + return exitval + def __init__(self): self._argparser = argparse.ArgumentParser() self._setup_argparser(self._argparser) @@ -116,7 +139,7 @@ class Program(object): # pylint: disable=too-few-public-methods return exitval if cmd_args.merge_apps: for app in cmd_args.merge_apps: - exitval = self._add_installed_apps_from_app(cmd_args.project_dir, app) + exitval = self._merge_app(cmd_args.project_dir, app) if exitval != os.EX_OK: return exitval return exitval diff --git a/src/django_deploy/tests/django_settings.py b/src/django_deploy/tests/django_settings.py deleted file mode 100644 index d4f2f21..0000000 --- a/src/django_deploy/tests/django_settings.py +++ /dev/null @@ -1 +0,0 @@ -ADD_INSTALLED_APPS = ['fake_app1', 'fake_app2'] diff --git a/src/django_deploy/tests/fake_app/__init__.py b/src/django_deploy/tests/fake_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/django_deploy/tests/fake_app/django_settings.py b/src/django_deploy/tests/fake_app/django_settings.py new file mode 100644 index 0000000..b46f77e --- /dev/null +++ b/src/django_deploy/tests/fake_app/django_settings.py @@ -0,0 +1,4 @@ +ADD_INSTALLED_APPS = ['django_deploy.tests.fake_app'] +ADD_URLPATTERNS = [ + {'type': 'include', 'pattern': 'fake/', 'module': 'django_deploy.tests.fake_app.urls'}, +] diff --git a/src/django_deploy/tests/fake_app/urls.py b/src/django_deploy/tests/fake_app/urls.py new file mode 100644 index 0000000..85d5294 --- /dev/null +++ b/src/django_deploy/tests/fake_app/urls.py @@ -0,0 +1,2 @@ +app_name = 'fake1' +urlpatterns = [] \ No newline at end of file diff --git a/src/django_deploy/tests/test_program.py b/src/django_deploy/tests/test_program.py index ff5196e..1ee4d9d 100644 --- a/src/django_deploy/tests/test_program.py +++ b/src/django_deploy/tests/test_program.py @@ -4,7 +4,9 @@ import sys import unittest import pytest -from ..config import DJANGO_SETTINGS_MODULE_NAME +from unittest import mock + +from ..config import DJANGO_SETTINGS_DIR from ..program import Program @@ -24,7 +26,7 @@ class MainTestCase(unittest.TestCase): self.assertEqual(os.EX_OK, exitval, 'program() does not return os.EX_OK') self.assertTrue(os.path.isdir(project_dir), 'project directory was not created') - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME) + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) self.assertTrue(os.path.isdir(settings_dir), 'settings directory was not created') settings_file = os.path.join(settings_dir, 'settings.py') self.assertTrue(os.path.isfile(settings_file), 'settings.py was not created') @@ -44,6 +46,12 @@ class MainTestCase(unittest.TestCase): tmpdir = self.tmpdir project_dir = os.path.join(str(tmpdir), 'env', 'django') + argv = ['-c', project_dir] + self._program(argv=argv) + + argv = ['-a', 'django_deploy', '-a', 'django_deploy.tests.fake_app', project_dir] + self._program(argv=argv) + expected_installed_apps = [ 'django.contrib.admin', 'django.contrib.auth', @@ -51,17 +59,10 @@ class MainTestCase(unittest.TestCase): 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'fake_app1', - 'fake_app2', + 'django_deploy.tests.fake_app', ] - argv = ['-c', project_dir] - self._program(argv=argv) - - argv = ['-a', 'django_deploy', '-a', 'django_deploy.tests', project_dir] - self._program(argv=argv) - - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME) + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) sys.path.insert(0, settings_dir) settings = importlib.import_module('settings') if sys.version_info.major == 2: # pragma: no cover @@ -69,4 +70,54 @@ class MainTestCase(unittest.TestCase): else: # pragma: no cover importlib.reload(settings) # pylint: disable=no-member sys.path.pop(0) + self.assertListEqual(expected_installed_apps, settings.INSTALLED_APPS) + + def test_merge_root_urlconf(self): + tmpdir = self.tmpdir + project_dir = os.path.join(str(tmpdir), 'env', 'django') + + argv = ['-c', project_dir] + self._program(argv=argv) + + argv = ['-a', 'django_deploy', '-a', 'django_deploy.tests.fake_app', project_dir] + self._program(argv=argv) + + if 'django.contrib.admin' in sys.modules: + original_admin = sys.modules['django.contrib.admin'] + else: + original_admin = None + mock_admin = mock.Mock() + sys.modules['django.contrib.admin'] = mock_admin + + from django.urls import path + + expected_urlpatterns = [ + ('URLPattern', 'admin/', mock_admin.site.urls), + ('URLResolver', 'fake/'), + ] + + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) + sys.path.insert(0, settings_dir) + root_urlconf = importlib.import_module('urls') + if sys.version_info.major == 2: # pragma: no cover + reload(root_urlconf) # pylint: disable=undefined-variable + else: # pragma: no cover + importlib.reload(root_urlconf) # pylint: disable=no-member + sys.path.pop(0) + + if original_admin: + sys.modules['django.contrib.admin'] = original_admin + + real_urlpatterns = root_urlconf.urlpatterns + self.assertEqual(len(expected_urlpatterns), len(real_urlpatterns)) + + for i in range(0, len(expected_urlpatterns)): + expected = expected_urlpatterns[i] + real = real_urlpatterns[i] + self.assertEqual(expected[0], real.__class__.__name__) + if expected[0] == 'URLPattern': + self.assertIsNotNone(real.pattern.match(expected[1])) + self.assertEqual(expected[2], real.callback) + else: + raise Exception(dir(real)) diff --git a/src/django_deploy/tests/test_settings.py b/src/django_deploy/tests/test_settings.py new file mode 100644 index 0000000..49dbf12 --- /dev/null +++ b/src/django_deploy/tests/test_settings.py @@ -0,0 +1,4 @@ +SECRET_KEY = 'test_settings' +INSTALLED_APPS = [ + 'django_deploy.tests.fake_app', +] diff --git a/src/django_deploy/tests/test_utils.py b/src/django_deploy/tests/test_utils.py new file mode 100644 index 0000000..5229888 --- /dev/null +++ b/src/django_deploy/tests/test_utils.py @@ -0,0 +1,16 @@ +import unittest + +from ..utils import DjangoEnvironment + + +class DjangoEnvironmentTestCase(unittest.TestCase): + def test_django_environment(self): + settings_module_name = 'django_deploy.tests.test_settings' + + with DjangoEnvironment(settings_module_name=settings_module_name) as env: + check_attrs = [ + ('SECRET_KEY', 'test_settings'), + ('INSTALLED_APPS', ['django_deploy.tests.fake_app']), + ] + for key, value in check_attrs: + self.assertEqual(value, getattr(env.settings, key)) diff --git a/src/django_deploy/utils.py b/src/django_deploy/utils.py new file mode 100644 index 0000000..b8f6a42 --- /dev/null +++ b/src/django_deploy/utils.py @@ -0,0 +1,41 @@ +import importlib +import os +import sys +import django + +from .config import DJANGO_SETTINGS_DIR + + +class DjangoEnvironment(object): + def __init__(self, project_dir=None, settings_module_name=None): + self.project_dir = project_dir + + if settings_module_name is not None: + self.settings_module_name = settings_module_name + else: + self.settings_module_name = '{}.settings'.format(DJANGO_SETTINGS_DIR) + + self._original_sys_path = None + self._modified_sys_path = None + + def __enter__(self): + if self.project_dir: + self._original_sys_path = sys.path + sys.path.insert(0, self.project_dir) + self._modified_sys_path = sys.path + + print('Debug: %s' % self.settings_module_name) + os.environ['DJANGO_SETTINGS_MODULE'] = self.settings_module_name + django.setup() + + from django.conf import settings + self.settings = settings + if hasattr(settings, 'ROOT_URLCONF'): + self.root_urlconf = importlib.import_module(settings.ROOT_URLCONF) + + return self + + def __exit__(self, *args): + if self._modified_sys_path is not None and self._original_sys_path is not None: + if sys.path == self._modified_sys_path: + sys.path = self._original_sys_path