From 8e862334310308f1ee3a6526ad0398de9e8ced1e Mon Sep 17 00:00:00 2001 From: Jens Kleineheismann Date: Thu, 7 Nov 2019 17:01:53 +0100 Subject: [PATCH] it works. but coverage is too low. --- .pylintrc | 2 +- setup.py | 1 + src/django_deploy/program.py | 130 +++++++++++++----- .../tests/fake_app/django_settings.py | 2 +- src/django_deploy/tests/fake_app/urls.py | 3 +- src/django_deploy/tests/test_program.py | 60 ++++---- src/django_deploy/tests/test_utils.py | 16 --- src/django_deploy/utils.py | 50 +++---- tox.ini | 5 +- 9 files changed, 151 insertions(+), 118 deletions(-) delete mode 100644 src/django_deploy/tests/test_utils.py diff --git a/.pylintrc b/.pylintrc index 210b081..b70bbbb 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,7 +1,7 @@ [MASTER] persistent=no -#load-plugins=pylint_django +load-plugins=pylint_django [MESSAGES CONTROL] diff --git a/setup.py b/setup.py index dadfe32..d7c4b44 100644 --- a/setup.py +++ b/setup.py @@ -79,5 +79,6 @@ setup( }, install_requires=[ 'django', + 'mock', ], ) diff --git a/src/django_deploy/program.py b/src/django_deploy/program.py index e1d3a92..996788b 100644 --- a/src/django_deploy/program.py +++ b/src/django_deploy/program.py @@ -5,6 +5,7 @@ import os import sys from .config import DJANGO_SETTINGS_DIR +from .utils import get_root_urlconf from .version import VERSION @@ -36,18 +37,6 @@ class Program(object): # pylint: disable=too-few-public-methods return self._argparser.parse_args(argv) - @staticmethod - def _append_to_settings(project_dir, code, comment): - 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' - - text = '\n' + comment + '\n' + code + '\n' - with open(settings_file, 'a') as f: - f.write(text) - if os.path.isfile(settings_file_py2cache): - os.unlink(settings_file_py2cache) # pragma: no cover - @staticmethod def _install_django_files(project_dir, overwrite=False): if os.path.exists(project_dir): @@ -64,33 +53,63 @@ class Program(object): # pylint: disable=too-few-public-methods exitval = os.system(cmd) return exitval - def _add_installed_apps_from_app(self, project_dir, app): - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) + @staticmethod + def _append_to_pythonfile(path, text): + py2_cache = path + 'c' + with open(path, 'a') as f: + f.write(text) + if os.path.isfile(py2_cache): + os.unlink(py2_cache) # pragma: no cover + def _append_to_settings(self, project_dir, code, comment): + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) + settings_file = os.path.join(settings_dir, 'settings.py') + + text = '\n' + comment + '\n' + code + '\n' + return self._append_to_pythonfile(settings_file, text) + + def _append_to_urlconf(self, project_dir, code, comment): + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) + settings_file = os.path.join(settings_dir, 'urls.py') + + text = '\n' + comment + '\n' + code + '\n' + return self._append_to_pythonfile(settings_file, text) + + def _add_installed_apps_from_app(self, project_dir, app): settings_from_app = importlib.import_module('{}.django_settings'.format(app)) if hasattr(settings_from_app, 'ADD_INSTALLED_APPS'): - add_apps = settings_from_app.ADD_INSTALLED_APPS + wanted_apps = settings_from_app.ADD_INSTALLED_APPS else: - add_apps = [] + wanted_apps = [] - if not add_apps: - sys.stdout.write('{}: do not care about INSTALLED_APPS\n'.format(app)) + if wanted_apps: + sys.stdout.write('{app}: wanted apps: {apps}\n'.format(app=app, apps=', '.join(wanted_apps))) + else: return os.EX_OK + settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) sys.path.insert(0, settings_dir) settings = importlib.import_module('settings') sys.path.pop(0) + already_apps = [] missing_apps = [] - for add_app in add_apps: - if add_app not in settings.INSTALLED_APPS: - missing_apps.append(add_app) + for wanted_app in wanted_apps: + if wanted_app in settings.INSTALLED_APPS: + already_apps.append(wanted_app) + else: + missing_apps.append(wanted_app) + + if already_apps: + sys.stdout.write('{app}: already in settings.INSTALLED_APPS:' + ' {apps}\n'.format(app=app, apps=', '.join(already_apps))) if missing_apps: - sys.stdout.write('{app}: adding {apps} to INSTALLED_APPS\n'.format(app=app, apps=missing_apps)) + sys.stdout.write('{app}: adding to settings.INSTALLED_APPS:' + ' {apps}\n'.format(app=app, apps=', '.join(missing_apps))) code = 'INSTALLED_APPS += [\n' - for add_app in missing_apps: - code += ' \'{}\',\n'.format(add_app) + for missing_app in missing_apps: + code += ' \'{}\',\n'.format(missing_app) code += ']\n' timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') comment = '### {app}: added apps with django-deploy at {timestamp}'.format(app=app, @@ -98,25 +117,68 @@ class Program(object): # pylint: disable=too-few-public-methods self._append_to_settings(project_dir, code, comment) else: - sys.stdout.write('{app}: INSTALLED_APPS is fine\n'.format(app=app)) + sys.stdout.write('{app}: all wanted apps are already in settings.INSTALLED_APPS\n'.format(app=app)) return os.EX_OK - def _add_urlpatterns_from_app(self, project_dir, app): - settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR) - + def _add_urlpatterns_from_app(self, project_dir, app): # pylint: disable=too-many-locals,too-many-branches settings_from_app = importlib.import_module('{}.django_settings'.format(app)) if hasattr(settings_from_app, 'ADD_URLPATTERNS'): - add_urls = settings_from_app.ADD_URLPATTERNS + wanted_urls = {} + for wanted in settings_from_app.ADD_URLPATTERNS: + wanted_urls[wanted['pattern']] = wanted + wanted_patterns = wanted_urls.keys() else: - add_urls = [] + wanted_urls = {} + wanted_patterns = [] - if not add_urls: - sys.stdout.write('{}: do not care about ROOT_URLCONF\n'.format(app)) + if wanted_urls: + sys.stdout.write('{app}: wanted urlpatterns: {urls}\n'.format(app=app, urls=', '.join(wanted_patterns))) + else: return os.EX_OK - raise NotImplementedError() - self._append_to_settings(project_dir, code, comment) + root_urlconf = get_root_urlconf(project_dir) + + already_patterns = [] + for item in root_urlconf.urlpatterns: + if hasattr(item, 'pattern'): + pattern = str(item.pattern) + else: + pattern = item.regex.pattern + if pattern in wanted_patterns: + already_patterns.append(pattern) + + missing_patterns = [pattern for pattern in wanted_patterns if pattern not in already_patterns] + + if already_patterns: + sys.stdout.write('{app}: already in urlpatterns:' + ' {urls}\n'.format(app=app, urls=', '.join(already_patterns))) + + if missing_patterns: + sys.stdout.write('{app}: adding to urlpatterns:' + ' {apps}\n'.format(app=app, apps=', '.join(missing_patterns))) + code = '' + if not hasattr(root_urlconf, 'url'): + code += 'from django.conf.urls import url\n' + if not hasattr(root_urlconf, 'include'): + code += 'from django.conf.urls import include\n' + code += 'urlpatterns += [\n' + for pattern in missing_patterns: + url = wanted_urls[pattern] + if url['type'] == 'include': + code += " url(r'{pattern}', include('{module}')),\n".format(pattern=pattern, + module=url['module']) + else: + sys.stderr.write('{app}: not a supported url type: {type}\n'.format(app=app, type=url['type'])) + code += ']\n' + timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + comment = '### {app}: added urlpatterns with django-deploy at {timestamp}'.format(app=app, + timestamp=timestamp) + self._append_to_urlconf(project_dir, code, comment) + else: + sys.stdout.write('{app}: all wanted patterns are already in urlconfig\n'.format(app=app)) + + return os.EX_OK def _merge_app(self, project_dir, app): exitval = self._add_installed_apps_from_app(project_dir, app) diff --git a/src/django_deploy/tests/fake_app/django_settings.py b/src/django_deploy/tests/fake_app/django_settings.py index b46f77e..23e6a92 100644 --- a/src/django_deploy/tests/fake_app/django_settings.py +++ b/src/django_deploy/tests/fake_app/django_settings.py @@ -1,4 +1,4 @@ ADD_INSTALLED_APPS = ['django_deploy.tests.fake_app'] ADD_URLPATTERNS = [ - {'type': 'include', 'pattern': 'fake/', 'module': 'django_deploy.tests.fake_app.urls'}, + {'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 index 85d5294..0d2edeb 100644 --- a/src/django_deploy/tests/fake_app/urls.py +++ b/src/django_deploy/tests/fake_app/urls.py @@ -1,2 +1,3 @@ +# pylint: skip-file app_name = 'fake1' -urlpatterns = [] \ No newline at end of file +urlpatterns = [] diff --git a/src/django_deploy/tests/test_program.py b/src/django_deploy/tests/test_program.py index 1ee4d9d..cfdc511 100644 --- a/src/django_deploy/tests/test_program.py +++ b/src/django_deploy/tests/test_program.py @@ -4,10 +4,9 @@ import sys import unittest import pytest -from unittest import mock - from ..config import DJANGO_SETTINGS_DIR from ..program import Program +from ..utils import get_root_urlconf class MainTestCase(unittest.TestCase): @@ -83,41 +82,38 @@ class MainTestCase(unittest.TestCase): 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'] + root_urlconf = get_root_urlconf(project_dir) + + if hasattr(root_urlconf, 'path'): + expected_urlpatterns = [ + ('URLPattern', 'admin/', 'django.contrib.admin.site.urls'), + ] 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/'), + expected_urlpatterns = [ + ('URLPattern', '^admin/', 'django.contrib.admin.site.urls'), + ] + expected_urlpatterns += [ + ('URLResolver', '^fake/', 'django_deploy.tests.fake_app.urls'), ] - 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] + for i, expected in enumerate(expected_urlpatterns): 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) + real_class_name = real.__class__.__name__ + self.assertTrue(real_class_name.endswith(expected[0])) + if real_class_name == 'URLPattern': + self.assertEqual(expected[1], str(real.pattern)) + self.assertTrue(real.callback.startswith("