Merge branch 'issue_1_heinzel' of python/django-deploy into master
All checks were successful
buildbot/tox Build done.
All checks were successful
buildbot/tox Build done.
This commit was merged in pull request #2.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
[MASTER]
|
[MASTER]
|
||||||
|
|
||||||
persistent=no
|
persistent=no
|
||||||
#load-plugins=pylint_django
|
load-plugins=pylint_django
|
||||||
|
|
||||||
|
|
||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
|
|||||||
1
setup.py
1
setup.py
@@ -79,5 +79,6 @@ setup(
|
|||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'django',
|
'django',
|
||||||
|
'mock',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
from .config import get_installed_apps, get_urlpatterns
|
||||||
from .main import main
|
from .main import main
|
||||||
|
|||||||
@@ -1 +1,102 @@
|
|||||||
DJANGO_SETTINGS_MODULE_NAME = 'main'
|
try:
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
from collections import MutableMapping
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
|
DJANGO_SETTINGS_DIR = 'main'
|
||||||
|
|
||||||
|
|
||||||
|
class _BaseDict(MutableMapping):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(_BaseDict, self).__init__(*args, **kwargs)
|
||||||
|
self._store = dict()
|
||||||
|
self.update(dict(*args, **kwargs))
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._store[self.__keytransform__(key)]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self._store[self.__keytransform__(key)] = value
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
del self._store[self.__keytransform__(key)]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._store)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._store)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __keytransform__(key):
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
class DeployedAppsConfig(_BaseDict): # pylint: disable=too-many-ancestors
|
||||||
|
def load(self, path=None):
|
||||||
|
if path is None:
|
||||||
|
path = self._json_file
|
||||||
|
if os.path.exists(path):
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
self._store = json.load(f)
|
||||||
|
else:
|
||||||
|
self._store = dict()
|
||||||
|
|
||||||
|
def write(self, path=None):
|
||||||
|
if path is None:
|
||||||
|
path = self._json_file
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
json.dump(self._store, f, indent=4)
|
||||||
|
|
||||||
|
def __init__(self, project_dir=None, settings_dir=None):
|
||||||
|
assert (project_dir or settings_dir), 'DeployedAppsConfig(): ' \
|
||||||
|
'Either keyword argument project_dir or settings_dir' \
|
||||||
|
' must be set.'
|
||||||
|
assert (not (project_dir and settings_dir)), 'DeployedAppsConfig(): ' \
|
||||||
|
'Keyword arguments project_dir and settings_dir' \
|
||||||
|
' are mutually exclusive.'
|
||||||
|
super(DeployedAppsConfig, self).__init__()
|
||||||
|
if project_dir is not None:
|
||||||
|
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||||
|
self._json_file = os.path.join(settings_dir, 'django_deploy.json')
|
||||||
|
self.load()
|
||||||
|
|
||||||
|
|
||||||
|
def get_installed_apps(file_path, installed_apps):
|
||||||
|
settings_dir = os.path.dirname(os.path.abspath(file_path))
|
||||||
|
config = DeployedAppsConfig(settings_dir=settings_dir)
|
||||||
|
app_list = installed_apps[:]
|
||||||
|
for wanting_app in config:
|
||||||
|
wanted_apps = config[wanting_app].get('INSTALLED_APPS', [])
|
||||||
|
for wanted_app in wanted_apps:
|
||||||
|
if wanted_app not in app_list:
|
||||||
|
app_list.append(wanted_app)
|
||||||
|
return app_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_urlpatterns(file_path, urlpatterns):
|
||||||
|
settings_dir = os.path.dirname(os.path.abspath(file_path))
|
||||||
|
config = DeployedAppsConfig(settings_dir=settings_dir)
|
||||||
|
urls_list = urlpatterns[:]
|
||||||
|
patterns = []
|
||||||
|
for url_obj in urls_list:
|
||||||
|
# Django 1 vs Django 2
|
||||||
|
if url_obj.__class__.__name__.startswith('Regex'): # pragma: no cover
|
||||||
|
patterns.append(url_obj.regex.pattern)
|
||||||
|
else: # pragma: no cover
|
||||||
|
patterns.append(str(url_obj.pattern))
|
||||||
|
for wanting_app in config:
|
||||||
|
wanted_urls = config[wanting_app].get('urlpatterns', [])
|
||||||
|
for wanted_url in wanted_urls:
|
||||||
|
pattern = wanted_url['pattern']
|
||||||
|
if pattern in patterns:
|
||||||
|
continue
|
||||||
|
if wanted_url['type'] == 'include':
|
||||||
|
url_obj = url(pattern, include(wanted_url['module']))
|
||||||
|
urls_list.append(url_obj)
|
||||||
|
patterns.append(pattern)
|
||||||
|
return urls_list
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
# ADD_INSTALLED_APPS = ['django_deploy']
|
# ADD_INSTALLED_APPS = ['django_deploy']
|
||||||
|
# ADD_URLPATTERNS = [
|
||||||
|
# {'type': 'include', 'pattern': '', 'module': 'django_deploy.urls'},
|
||||||
|
# ]
|
||||||
|
|||||||
8
src/django_deploy/exceptions.py
Normal file
8
src/django_deploy/exceptions.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class FatalError(Exception):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._exitval = kwargs.pop('exitval', None)
|
||||||
|
super(FatalError, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def exitval(self):
|
||||||
|
return self._exitval
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import datetime
|
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .config import DJANGO_SETTINGS_MODULE_NAME
|
from .config import DJANGO_SETTINGS_DIR, DeployedAppsConfig
|
||||||
|
from .exceptions import FatalError
|
||||||
from .version import VERSION
|
from .version import VERSION
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +23,10 @@ class Program(object): # pylint: disable=too-few-public-methods
|
|||||||
action='store_true', dest='create',
|
action='store_true', dest='create',
|
||||||
help='Create the django project directory')
|
help='Create the django project directory')
|
||||||
|
|
||||||
|
parser.add_argument('-e', '--enable',
|
||||||
|
action='store_true', dest='enable',
|
||||||
|
help='Enable django_deploy in an existing django project directory')
|
||||||
|
|
||||||
parser.add_argument('project_dir', metavar='PATH',
|
parser.add_argument('project_dir', metavar='PATH',
|
||||||
help='The directory, where the django project is or will be installed.')
|
help='The directory, where the django project is or will be installed.')
|
||||||
|
|
||||||
@@ -37,70 +41,93 @@ class Program(object): # pylint: disable=too-few-public-methods
|
|||||||
return self._argparser.parse_args(argv)
|
return self._argparser.parse_args(argv)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _append_to_settings(project_dir, code, comment):
|
def _append_to_pythonfile(path, text):
|
||||||
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME)
|
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')
|
settings_file = os.path.join(settings_dir, 'settings.py')
|
||||||
settings_file_py2cache = settings_file + 'c'
|
|
||||||
|
|
||||||
text = '\n' + comment + '\n' + code + '\n'
|
text = '\n' + comment + '\n' + code + '\n'
|
||||||
with open(settings_file, 'a') as f:
|
return self._append_to_pythonfile(settings_file, text)
|
||||||
f.write(text)
|
|
||||||
if os.path.isfile(settings_file_py2cache):
|
def _append_to_urlconf(self, project_dir, code, comment):
|
||||||
os.unlink(settings_file_py2cache) # pragma: no cover
|
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)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _install_django_files(project_dir, overwrite=False):
|
def _install_django_files(project_dir, overwrite=False):
|
||||||
if os.path.exists(project_dir):
|
if not os.path.exists(project_dir):
|
||||||
if not overwrite:
|
|
||||||
sys.stderr.write('directory already exists: {}\n'.format(project_dir))
|
|
||||||
return os.EX_NOPERM
|
|
||||||
else:
|
|
||||||
os.makedirs(project_dir)
|
os.makedirs(project_dir)
|
||||||
|
|
||||||
settings_module = DJANGO_SETTINGS_MODULE_NAME
|
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||||
cmd = 'django-admin startproject {name} {path}'.format(name=settings_module,
|
if os.path.exists(settings_dir) and not overwrite:
|
||||||
|
sys.stderr.write('directory already exists: {}\n'.format(settings_dir))
|
||||||
|
raise FatalError(exitval=os.EX_NOPERM)
|
||||||
|
|
||||||
|
cmd = 'django-admin startproject {name} {path}'.format(name=DJANGO_SETTINGS_DIR,
|
||||||
path=project_dir)
|
path=project_dir)
|
||||||
sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir))
|
sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir))
|
||||||
exitval = os.system(cmd)
|
exitval = os.system(cmd)
|
||||||
return exitval
|
if exitval != os.EX_OK: # pragma: no cover
|
||||||
|
raise FatalError(exitval=exitval)
|
||||||
|
|
||||||
def _add_installed_apps_from_app(self, project_dir, app):
|
@staticmethod
|
||||||
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME)
|
def _install_django_deploy_files(project_dir, overwrite=False):
|
||||||
|
json_file = os.path.join(project_dir, DJANGO_SETTINGS_DIR, 'django_deploy.json')
|
||||||
|
if os.path.exists(json_file) and not overwrite:
|
||||||
|
sys.stderr.write('file already exists: {}\n'.format(json_file))
|
||||||
|
raise FatalError(exitval=os.EX_NOPERM)
|
||||||
|
|
||||||
|
config = DeployedAppsConfig(project_dir=project_dir)
|
||||||
|
config.write()
|
||||||
|
|
||||||
|
def _enable_django_deploy(self, project_dir):
|
||||||
|
settings_code = ''
|
||||||
|
settings_code += 'from django_deploy import get_installed_apps\n'
|
||||||
|
settings_code += 'INSTALLED_APPS = get_installed_apps(__file__, INSTALLED_APPS)\n'
|
||||||
|
settings_comment = '# django-deploy'
|
||||||
|
self._append_to_settings(project_dir, settings_code, settings_comment)
|
||||||
|
urlconf_code = ''
|
||||||
|
urlconf_code += 'from django_deploy import get_urlpatterns\n'
|
||||||
|
urlconf_code += 'urlpatterns = get_urlpatterns(__file__, urlpatterns)\n'
|
||||||
|
urlconf_comment = '# django-deploy'
|
||||||
|
self._append_to_urlconf(project_dir, urlconf_code, urlconf_comment)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _add_installed_apps_from_app(project_dir, app):
|
||||||
settings_from_app = importlib.import_module('{}.django_settings'.format(app))
|
settings_from_app = importlib.import_module('{}.django_settings'.format(app))
|
||||||
if hasattr(settings_from_app, 'ADD_INSTALLED_APPS'):
|
wanted_apps = getattr(settings_from_app, 'ADD_INSTALLED_APPS', [])
|
||||||
add_apps = settings_from_app.ADD_INSTALLED_APPS
|
|
||||||
else:
|
|
||||||
add_apps = []
|
|
||||||
|
|
||||||
if not add_apps:
|
config = DeployedAppsConfig(project_dir=project_dir)
|
||||||
sys.stdout.write('{}: do not care about INSTALLED_APPS\n'.format(app))
|
if app not in config:
|
||||||
return os.EX_OK
|
config[app] = {}
|
||||||
|
|
||||||
sys.path.insert(0, settings_dir)
|
config[app]['INSTALLED_APPS'] = wanted_apps
|
||||||
settings = importlib.import_module('settings')
|
config.write()
|
||||||
sys.path.pop(0)
|
|
||||||
|
|
||||||
missing_apps = []
|
@staticmethod
|
||||||
for add_app in add_apps:
|
def _add_urlpatterns_from_app(project_dir, app):
|
||||||
if add_app not in settings.INSTALLED_APPS:
|
settings_from_app = importlib.import_module('{}.django_settings'.format(app))
|
||||||
missing_apps.append(add_app)
|
wanted_urls = getattr(settings_from_app, 'ADD_URLPATTERNS', [])
|
||||||
|
|
||||||
if missing_apps:
|
config = DeployedAppsConfig(project_dir=project_dir)
|
||||||
sys.stdout.write('{app}: adding {apps} to INSTALLED_APPS\n'.format(app=app, apps=missing_apps))
|
if app not in config: # pragma: no cover
|
||||||
code = 'INSTALLED_APPS += [\n'
|
config[app] = {}
|
||||||
for add_app in missing_apps:
|
|
||||||
code += ' \'{}\',\n'.format(add_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,
|
|
||||||
timestamp=timestamp)
|
|
||||||
|
|
||||||
self._append_to_settings(project_dir, code, comment)
|
config[app]['urlpatterns'] = wanted_urls
|
||||||
else:
|
config.write()
|
||||||
sys.stdout.write('{app}: INSTALLED_APPS is fine\n'.format(app=app))
|
|
||||||
|
|
||||||
return os.EX_OK
|
def _merge_app(self, project_dir, app):
|
||||||
|
self._add_installed_apps_from_app(project_dir, app)
|
||||||
|
self._add_urlpatterns_from_app(project_dir, app)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._argparser = argparse.ArgumentParser()
|
self._argparser = argparse.ArgumentParser()
|
||||||
@@ -110,13 +137,15 @@ class Program(object): # pylint: disable=too-few-public-methods
|
|||||||
argv = kwargs.get('argv', None)
|
argv = kwargs.get('argv', None)
|
||||||
cmd_args = self._parse_args(argv)
|
cmd_args = self._parse_args(argv)
|
||||||
exitval = os.EX_OK
|
exitval = os.EX_OK
|
||||||
|
try:
|
||||||
if cmd_args.create:
|
if cmd_args.create:
|
||||||
exitval = self._install_django_files(cmd_args.project_dir)
|
self._install_django_files(cmd_args.project_dir)
|
||||||
if exitval != os.EX_OK:
|
if cmd_args.create or cmd_args.enable:
|
||||||
return exitval
|
self._install_django_deploy_files(cmd_args.project_dir)
|
||||||
|
self._enable_django_deploy(cmd_args.project_dir)
|
||||||
if cmd_args.merge_apps:
|
if cmd_args.merge_apps:
|
||||||
for app in cmd_args.merge_apps:
|
for app in cmd_args.merge_apps:
|
||||||
exitval = self._add_installed_apps_from_app(cmd_args.project_dir, app)
|
self._merge_app(cmd_args.project_dir, app)
|
||||||
if exitval != os.EX_OK:
|
except FatalError as e:
|
||||||
return exitval
|
exitval = e.exitval
|
||||||
return exitval
|
return exitval
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
ADD_INSTALLED_APPS = ['fake_app1', 'fake_app2']
|
|
||||||
0
src/django_deploy/tests/fake_app1/__init__.py
Normal file
0
src/django_deploy/tests/fake_app1/__init__.py
Normal file
5
src/django_deploy/tests/fake_app1/django_settings.py
Normal file
5
src/django_deploy/tests/fake_app1/django_settings.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ADD_INSTALLED_APPS = ['django_deploy.tests.fake_app1']
|
||||||
|
ADD_URLPATTERNS = [
|
||||||
|
{'type': 'include', 'pattern': '^fake1/', 'module': 'django_deploy.tests.fake_app1.urls'},
|
||||||
|
{'type': 'invalid', 'pattern': '^invalid/'},
|
||||||
|
]
|
||||||
3
src/django_deploy/tests/fake_app1/urls.py
Normal file
3
src/django_deploy/tests/fake_app1/urls.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# pylint: skip-file
|
||||||
|
app_name = 'fake1'
|
||||||
|
urlpatterns = []
|
||||||
0
src/django_deploy/tests/fake_app2/__init__.py
Normal file
0
src/django_deploy/tests/fake_app2/__init__.py
Normal file
5
src/django_deploy/tests/fake_app2/django_settings.py
Normal file
5
src/django_deploy/tests/fake_app2/django_settings.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ADD_INSTALLED_APPS = ['django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app2']
|
||||||
|
ADD_URLPATTERNS = [
|
||||||
|
{'type': 'include', 'pattern': '^fake1/', 'module': 'django_deploy.tests.fake_app1.urls'},
|
||||||
|
{'type': 'include', 'pattern': '^fake2/', 'module': 'django_deploy.tests.fake_app2.urls'},
|
||||||
|
]
|
||||||
3
src/django_deploy/tests/fake_app2/urls.py
Normal file
3
src/django_deploy/tests/fake_app2/urls.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# pylint: skip-file
|
||||||
|
app_name = 'fake2'
|
||||||
|
urlpatterns = []
|
||||||
54
src/django_deploy/tests/test_config.py
Normal file
54
src/django_deploy/tests/test_config.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
|
from ..config import get_installed_apps, get_urlpatterns, DeployedAppsConfig
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionsTestCase(unittest.TestCase):
|
||||||
|
def test_get_installed_apps(self):
|
||||||
|
test_data_sets = [
|
||||||
|
(__file__, ['fake_app1', 'fake_app2']),
|
||||||
|
]
|
||||||
|
|
||||||
|
for file_path, installed_apps in test_data_sets:
|
||||||
|
result = get_installed_apps(file_path, installed_apps)
|
||||||
|
self.assertEqual(installed_apps, result)
|
||||||
|
|
||||||
|
def test_get_urlpatterns(self):
|
||||||
|
test_data_sets = [
|
||||||
|
(
|
||||||
|
__file__,
|
||||||
|
[
|
||||||
|
url('/app1', include('django_deploy.tests.fake_app1.urls')),
|
||||||
|
url('/app2', include('django_deploy.tests.fake_app2.urls')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
for file_path, urlpatterns in test_data_sets:
|
||||||
|
result = get_urlpatterns(file_path, urlpatterns)
|
||||||
|
self.assertEqual(urlpatterns, result)
|
||||||
|
|
||||||
|
|
||||||
|
class DeployedAppsConfigTestCase(unittest.TestCase):
|
||||||
|
def test_init_parameters(self):
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
DeployedAppsConfig()
|
||||||
|
with self.assertRaises(AssertionError):
|
||||||
|
DeployedAppsConfig(project_dir='.', settings_dir='.')
|
||||||
|
|
||||||
|
def test_len(self):
|
||||||
|
c = DeployedAppsConfig(settings_dir='.')
|
||||||
|
c['a'] = 'a'
|
||||||
|
c['b'] = 'b'
|
||||||
|
c['c'] = 'c'
|
||||||
|
self.assertEqual(3, len(c))
|
||||||
|
|
||||||
|
def test_del(self):
|
||||||
|
c = DeployedAppsConfig(settings_dir='.')
|
||||||
|
c['a'] = 'a'
|
||||||
|
c['b'] = 'b'
|
||||||
|
c['c'] = 'c'
|
||||||
|
del c['b']
|
||||||
|
self.assertEqual(2, len(c))
|
||||||
@@ -2,47 +2,104 @@ import importlib
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
import mock
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..config import DJANGO_SETTINGS_MODULE_NAME
|
from ..config import DJANGO_SETTINGS_DIR
|
||||||
from ..program import Program
|
from ..program import Program
|
||||||
|
|
||||||
|
|
||||||
class MainTestCase(unittest.TestCase):
|
class ProgramTestCase(unittest.TestCase):
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def tmpdir(self, tmpdir): # pylint: disable=method-hidden
|
def tmpdir(self, tmpdir): # pylint: disable=method-hidden
|
||||||
self.tmpdir = tmpdir
|
self.tmpdir = tmpdir
|
||||||
|
|
||||||
|
def _assert_django_project(self, project_dir):
|
||||||
|
self.assertTrue(os.path.isdir(project_dir), 'no directory: {}'.format(project_dir))
|
||||||
|
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||||
|
self.assertTrue(os.path.isdir(settings_dir), 'no directory: {}'.format(settings_dir))
|
||||||
|
settings_file = os.path.join(settings_dir, 'settings.py')
|
||||||
|
self.assertTrue(os.path.isfile(settings_file), 'no file: {}'.format(settings_file))
|
||||||
|
manage_script = os.path.join(project_dir, 'manage.py')
|
||||||
|
self.assertTrue(os.path.isfile(manage_script), 'no file: {}'.format(manage_script))
|
||||||
|
|
||||||
|
def _assert_django_deploy_project(self, project_dir):
|
||||||
|
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||||
|
settings_file = os.path.join(settings_dir, 'settings.py')
|
||||||
|
urlconf_file = os.path.join(settings_dir, 'urls.py')
|
||||||
|
django_deploy_json = os.path.join(settings_dir, 'django_deploy.json')
|
||||||
|
|
||||||
|
self.assertTrue(os.path.isfile(django_deploy_json), 'no file: {}'.format(django_deploy_json))
|
||||||
|
|
||||||
|
with open(settings_file, 'r') as f:
|
||||||
|
needle = 'from django_deploy import get_installed_apps'
|
||||||
|
haystack = f.read()
|
||||||
|
self.assertIn(needle, haystack)
|
||||||
|
|
||||||
|
with open(urlconf_file, 'r') as f:
|
||||||
|
needle = 'from django_deploy import get_urlpatterns'
|
||||||
|
haystack = f.read()
|
||||||
|
self.assertIn(needle, haystack)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._program = Program()
|
self._program = Program()
|
||||||
|
self._project_dir = os.path.join(str(self.tmpdir), 'django')
|
||||||
|
self._program(argv=['--create', self._project_dir])
|
||||||
|
|
||||||
def test_create(self):
|
def test_create_new_dir(self):
|
||||||
tmpdir = self.tmpdir
|
project_dir = os.path.join(str(self.tmpdir), 'new_dir')
|
||||||
project_dir = os.path.join(str(tmpdir), 'env', 'django')
|
|
||||||
|
|
||||||
exitval = self._program(argv=['-c', project_dir])
|
exitval = self._program(argv=['-c', project_dir])
|
||||||
|
|
||||||
self.assertEqual(os.EX_OK, exitval, 'program() does not return os.EX_OK')
|
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')
|
self._assert_django_project(project_dir)
|
||||||
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_MODULE_NAME)
|
self._assert_django_deploy_project(project_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')
|
|
||||||
manage_script = os.path.join(project_dir, 'manage.py')
|
|
||||||
self.assertTrue(os.path.isfile(manage_script), 'manage.py was not created')
|
|
||||||
|
|
||||||
def test_create_dont_overwrite(self):
|
def test_create_existing_empty_dir(self):
|
||||||
tmpdir = self.tmpdir
|
project_dir = os.path.join(str(self.tmpdir), 'empty_dir')
|
||||||
project_dir = os.path.join(str(tmpdir), 'env', 'django')
|
os.makedirs(project_dir)
|
||||||
|
exitval = self._program(argv=['-c', project_dir])
|
||||||
|
|
||||||
self._program(argv=['--create', project_dir])
|
self.assertEqual(os.EX_OK, exitval, 'program() does not return os.EX_OK')
|
||||||
exitval = self._program(argv=['--create', project_dir])
|
self._assert_django_project(project_dir)
|
||||||
|
self._assert_django_deploy_project(project_dir)
|
||||||
|
|
||||||
self.assertEqual(os.EX_NOPERM, exitval, 'second call to program() does not exit with os.EX_NOPERM')
|
def test_create_existing_project_dir(self):
|
||||||
|
exitval = self._program(argv=['-c', self._project_dir])
|
||||||
|
self.assertEqual(os.EX_NOPERM, exitval, 'program() does not return os.EX_NOPERM'
|
||||||
|
' when project directory is not empty')
|
||||||
|
|
||||||
|
def test_enable_django_deploy(self):
|
||||||
|
project_dir = os.path.join(str(self.tmpdir), 'pure_django')
|
||||||
|
os.makedirs(project_dir)
|
||||||
|
cmd = 'django-admin startproject {name} {path}'.format(name=DJANGO_SETTINGS_DIR,
|
||||||
|
path=project_dir)
|
||||||
|
os.system(cmd)
|
||||||
|
self._program(argv=['--enable', project_dir])
|
||||||
|
self._assert_django_deploy_project(project_dir)
|
||||||
|
|
||||||
|
def test_enable_django_deploy_twice(self):
|
||||||
|
exitval = self._program(argv=['-e', self._project_dir])
|
||||||
|
self.assertEqual(os.EX_NOPERM, exitval, 'program() does not return os.EX_NOPERM'
|
||||||
|
' when django_deploy is already enabled')
|
||||||
|
|
||||||
def test_merge_installed_apps(self):
|
def test_merge_installed_apps(self):
|
||||||
tmpdir = self.tmpdir
|
project_dir = self._project_dir
|
||||||
project_dir = os.path.join(str(tmpdir), 'env', 'django')
|
|
||||||
|
argv = [
|
||||||
|
'-a', 'django_deploy',
|
||||||
|
'-a', 'django_deploy.tests.fake_app1',
|
||||||
|
'-a', 'django_deploy.tests.fake_app2',
|
||||||
|
project_dir]
|
||||||
|
self._program(argv=argv)
|
||||||
|
|
||||||
|
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
|
||||||
|
reload(settings) # pylint: disable=undefined-variable
|
||||||
|
else: # pragma: no cover
|
||||||
|
importlib.reload(settings) # pylint: disable=no-member
|
||||||
|
sys.path.pop(0)
|
||||||
|
|
||||||
expected_installed_apps = [
|
expected_installed_apps = [
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
@@ -51,22 +108,66 @@ class MainTestCase(unittest.TestCase):
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'fake_app1',
|
'django_deploy.tests.fake_app1',
|
||||||
'fake_app2',
|
'django_deploy.tests.fake_app2',
|
||||||
]
|
]
|
||||||
|
|
||||||
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)
|
|
||||||
sys.path.insert(0, settings_dir)
|
|
||||||
settings = importlib.import_module('settings')
|
|
||||||
if sys.version_info.major == 2: # pragma: no cover
|
|
||||||
reload(settings) # pylint: disable=undefined-variable
|
|
||||||
else: # pragma: no cover
|
|
||||||
importlib.reload(settings) # pylint: disable=no-member
|
|
||||||
sys.path.pop(0)
|
|
||||||
self.assertListEqual(expected_installed_apps, settings.INSTALLED_APPS)
|
self.assertListEqual(expected_installed_apps, settings.INSTALLED_APPS)
|
||||||
|
|
||||||
|
def test_merge_root_urlconf(self):
|
||||||
|
project_dir = self._project_dir
|
||||||
|
|
||||||
|
argv = [
|
||||||
|
'-a', 'django_deploy',
|
||||||
|
'-a', 'django_deploy.tests.fake_app1',
|
||||||
|
'-a', 'django_deploy.tests.fake_app2',
|
||||||
|
project_dir]
|
||||||
|
self._program(argv=argv)
|
||||||
|
|
||||||
|
sys.modules['django.contrib'] = mock.MagicMock(name='django.contrib')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Django 2 vs Django 1
|
||||||
|
if hasattr(root_urlconf, 'path'): # pragma: no cover
|
||||||
|
expected_urlpatterns = [
|
||||||
|
('URLPattern', 'admin/', 'django.contrib.admin.site.urls'),
|
||||||
|
]
|
||||||
|
else: # pragma: no cover
|
||||||
|
expected_urlpatterns = [
|
||||||
|
('URLPattern', '^admin/', 'django.contrib.admin.site.urls'),
|
||||||
|
]
|
||||||
|
expected_urlpatterns += [
|
||||||
|
('URLResolver', '^fake1/', 'django_deploy.tests.fake_app1.urls'),
|
||||||
|
('URLResolver', '^fake2/', 'django_deploy.tests.fake_app2.urls'),
|
||||||
|
]
|
||||||
|
|
||||||
|
real_urlpatterns = root_urlconf.urlpatterns
|
||||||
|
self.assertEqual(len(expected_urlpatterns), len(real_urlpatterns))
|
||||||
|
|
||||||
|
for i, expected in enumerate(expected_urlpatterns):
|
||||||
|
real = real_urlpatterns[i]
|
||||||
|
real_class_name = real.__class__.__name__
|
||||||
|
self.assertTrue(real_class_name.endswith(expected[0]))
|
||||||
|
# Django 2 vs. Django 1
|
||||||
|
if real_class_name == 'URLPattern': # pragma: no cover
|
||||||
|
self.assertEqual(expected[1], str(real.pattern))
|
||||||
|
self.assertTrue(real.callback.startswith("<Mock name='{}' id=".format(expected[2])))
|
||||||
|
elif real_class_name == 'RegexURLPattern': # pragma: no cover
|
||||||
|
self.assertEqual(expected[1], real.regex.pattern)
|
||||||
|
self.assertTrue(real.callback.startswith("<Mock name='{}' id=".format(expected[2])))
|
||||||
|
elif real_class_name == 'URLResolver': # pragma: no cover
|
||||||
|
self.assertEqual(expected[1], str(real.pattern))
|
||||||
|
self.assertEqual(expected[2], real.urlconf_name.__name__)
|
||||||
|
elif real_class_name == 'RegexURLResolver': # pragma: no cover
|
||||||
|
self.assertEqual(expected[1], real.regex.pattern)
|
||||||
|
self.assertEqual(expected[2], real.urlconf_name.__name__)
|
||||||
|
else: # pragma: no cover
|
||||||
|
self.fail('Unknown urlpattern class: {}'.format(real_class_name))
|
||||||
|
|||||||
4
src/django_deploy/tests/test_settings.py
Normal file
4
src/django_deploy/tests/test_settings.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SECRET_KEY = 'test_settings'
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django_deploy.tests.fake_app',
|
||||||
|
]
|
||||||
5
tox.ini
5
tox.ini
@@ -3,8 +3,9 @@ envlist = py3,py2
|
|||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = coverage
|
deps = coverage
|
||||||
pylint
|
py2: pylint-django<2
|
||||||
|
py3: pylint-django
|
||||||
pytest
|
pytest
|
||||||
commands = coverage run -m pytest
|
commands = coverage run -m pytest
|
||||||
coverage report --skip-covered --fail-under=99
|
coverage report --skip-covered --fail-under=98
|
||||||
pylint django_deploy
|
pylint django_deploy
|
||||||
|
|||||||
Reference in New Issue
Block a user