Merge branch '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 #3.
This commit is contained in:
2
setup.py
2
setup.py
@@ -61,7 +61,7 @@ class CreatePythonEnvironment(MyCommand):
|
||||
|
||||
setup(
|
||||
name='django-deploy',
|
||||
version='0.1.dev0',
|
||||
version='0.2.dev0',
|
||||
description='Helper to deploy django apps.',
|
||||
url='https://dev.heinzelwerk.de/git/python/django-deploy',
|
||||
maintainer='Jens Kleineheismann',
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
from .config import get_installed_apps, get_urlpatterns
|
||||
from .hooks import get_installed_apps, get_urlpatterns
|
||||
from .main import main
|
||||
|
||||
167
src/django_deploy/api.py
Normal file
167
src/django_deploy/api.py
Normal file
@@ -0,0 +1,167 @@
|
||||
import errno
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .base_types import OrderedDict
|
||||
from .config import DJANGO_SETTINGS_DIR
|
||||
from .exceptions import DjangoDeployError
|
||||
|
||||
|
||||
class DjangoProjectHooksConfig(OrderedDict):
|
||||
def load(self, path=None):
|
||||
if path is None:
|
||||
path = self._json_file
|
||||
if os.path.exists(path):
|
||||
with open(path, 'r') as f:
|
||||
items = json.load(f)
|
||||
for item in items:
|
||||
app_name = item.pop('APP')
|
||||
self[app_name] = item
|
||||
else:
|
||||
self._clear()
|
||||
|
||||
def write(self, path=None):
|
||||
if path is None:
|
||||
path = self._json_file
|
||||
with open(path, 'w') as f:
|
||||
items = []
|
||||
for key in self:
|
||||
item = self[key]
|
||||
item['APP'] = key
|
||||
items.append(item)
|
||||
json.dump(items, 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(DjangoProjectHooksConfig, 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()
|
||||
|
||||
|
||||
class DjangoProject(object):
|
||||
_hooks_config_name = 'django_deploy_hooks'
|
||||
|
||||
@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 _get_hooks_config_from_app(self, module_path, hooks_config_name=None):
|
||||
if hooks_config_name is None:
|
||||
hooks_config_name = self._hooks_config_name
|
||||
|
||||
try:
|
||||
app_obj = importlib.import_module(module_path)
|
||||
except ImportError as e:
|
||||
raise DjangoDeployError(e, code=errno.ENOPKG)
|
||||
|
||||
try:
|
||||
hooks_config = getattr(app_obj, hooks_config_name)
|
||||
except AttributeError:
|
||||
try:
|
||||
hooks_config = importlib.import_module('{}.{}'.format(module_path, hooks_config_name))
|
||||
except ImportError as e:
|
||||
raise DjangoDeployError('In {}: {}'.format(module_path, e), code=errno.ENOENT)
|
||||
|
||||
return hooks_config
|
||||
|
||||
def create(self, install_hooks=True):
|
||||
project_dir = self._project_dir
|
||||
|
||||
if not os.path.exists(project_dir):
|
||||
os.makedirs(project_dir)
|
||||
|
||||
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||
if os.path.exists(settings_dir):
|
||||
raise DjangoDeployError('directory already exists: {}'.format(settings_dir), code=errno.EEXIST)
|
||||
|
||||
cmd = 'django-admin startproject {name} {path}'.format(name=DJANGO_SETTINGS_DIR,
|
||||
path=project_dir)
|
||||
sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir))
|
||||
exitval = os.system(cmd)
|
||||
if exitval != os.EX_OK: # pragma: no cover
|
||||
raise DjangoDeployError(exitval=exitval)
|
||||
|
||||
if install_hooks:
|
||||
self.install_hooks()
|
||||
|
||||
def install_hooks(self):
|
||||
project_dir = self._project_dir
|
||||
|
||||
if not os.path.exists(project_dir):
|
||||
raise DjangoDeployError('No such project directory: {}'.format(project_dir),
|
||||
code=errno.ENOENT)
|
||||
|
||||
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')
|
||||
json_file = os.path.join(settings_dir, 'django_deploy.json')
|
||||
|
||||
if not os.path.exists(settings_dir):
|
||||
raise DjangoDeployError('Existing project directory'
|
||||
' does not contain django project: {}'.format(project_dir),
|
||||
code=errno.EISDIR)
|
||||
|
||||
if os.path.exists(json_file):
|
||||
raise DjangoDeployError('file already exists: {}'.format(json_file), code=errno.EEXIST)
|
||||
|
||||
config = DjangoProjectHooksConfig(project_dir=project_dir)
|
||||
config.write()
|
||||
|
||||
text = '\n'
|
||||
text += '# django-deploy\n'
|
||||
text += 'from django_deploy import get_installed_apps\n'
|
||||
text += 'INSTALLED_APPS = get_installed_apps(__file__, INSTALLED_APPS)\n'
|
||||
text += '\n'
|
||||
self._append_to_pythonfile(settings_file, text)
|
||||
|
||||
text = '\n'
|
||||
text += '# django-deploy\n'
|
||||
text += 'from django_deploy import get_urlpatterns\n'
|
||||
text += 'urlpatterns = get_urlpatterns(__file__, urlpatterns)\n'
|
||||
text += '\n'
|
||||
self._append_to_pythonfile(urlconf_file, text)
|
||||
|
||||
def install_app(self, module_path, hooks_config_name=None):
|
||||
hooks_config = self._get_hooks_config_from_app(module_path, hooks_config_name)
|
||||
installed_apps = getattr(hooks_config, 'INSTALLED_APPS', [])
|
||||
|
||||
config = DjangoProjectHooksConfig(project_dir=self._project_dir)
|
||||
if module_path not in config:
|
||||
config[module_path] = {}
|
||||
|
||||
config[module_path]['INSTALLED_APPS'] = installed_apps
|
||||
config.write()
|
||||
|
||||
def mount_app(self, module_path, route, hooks_config_name=None):
|
||||
self.install_app(module_path, hooks_config_name=hooks_config_name)
|
||||
hooks_config = self._get_hooks_config_from_app(module_path, hooks_config_name)
|
||||
urlconf = getattr(hooks_config, 'ROOT_URLCONF', None)
|
||||
|
||||
if urlconf is None:
|
||||
raise DjangoDeployError('django deploy hooks from {} has no ROOT_URLCONF'.format(module_path),
|
||||
code=errno.ENOLINK)
|
||||
|
||||
config = DjangoProjectHooksConfig(project_dir=self._project_dir)
|
||||
if module_path not in config: # pragma: no cover
|
||||
config[module_path] = {}
|
||||
|
||||
config[module_path]['MOUNT'] = [route, urlconf]
|
||||
config.write()
|
||||
|
||||
def __init__(self, project_dir):
|
||||
self._project_dir = project_dir
|
||||
29
src/django_deploy/base_types.py
Normal file
29
src/django_deploy/base_types.py
Normal file
@@ -0,0 +1,29 @@
|
||||
class OrderedDict(object):
|
||||
def _clear(self):
|
||||
self._items = {}
|
||||
self._order = []
|
||||
|
||||
def __init__(self):
|
||||
self._items = {}
|
||||
self._order = []
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._items
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._items[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._items[key] = value
|
||||
if key not in self._order:
|
||||
self._order.append(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self._items[key]
|
||||
self._order.remove(key)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._order)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._items)
|
||||
@@ -1,102 +1 @@
|
||||
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
|
||||
|
||||
2
src/django_deploy/django_deploy_hooks.py
Normal file
2
src/django_deploy/django_deploy_hooks.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# INSTALLED_APPS = ['django_deploy']
|
||||
# ROOT_URLCONF = '.urls'
|
||||
@@ -1,4 +0,0 @@
|
||||
# ADD_INSTALLED_APPS = ['django_deploy']
|
||||
# ADD_URLPATTERNS = [
|
||||
# {'type': 'include', 'pattern': '', 'module': 'django_deploy.urls'},
|
||||
# ]
|
||||
@@ -1,7 +1,17 @@
|
||||
class FatalError(Exception):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._exitval = kwargs.pop('exitval', None)
|
||||
super(FatalError, self).__init__(*args, **kwargs)
|
||||
class DjangoDeployError(Exception):
|
||||
def __init__(self, message=None, code=None, exitval=None):
|
||||
self._message = message
|
||||
self._code = code
|
||||
self._exitval = exitval
|
||||
super(DjangoDeployError, self).__init__(message)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
return self._message
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
return self._code
|
||||
|
||||
@property
|
||||
def exitval(self):
|
||||
|
||||
31
src/django_deploy/hooks.py
Normal file
31
src/django_deploy/hooks.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import os
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from .api import DjangoProjectHooksConfig
|
||||
|
||||
|
||||
def get_installed_apps(file_path, installed_apps):
|
||||
settings_dir = os.path.dirname(os.path.abspath(file_path))
|
||||
config = DjangoProjectHooksConfig(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 = DjangoProjectHooksConfig(settings_dir=settings_dir)
|
||||
urls_list = urlpatterns[:]
|
||||
for app in config:
|
||||
if 'MOUNT' in config[app]:
|
||||
route, urlconf_module = config[app]['MOUNT']
|
||||
pattern = '^{}/'.format(route)
|
||||
if urlconf_module.startswith('.'):
|
||||
urlconf_module = '{}{}'.format(app, urlconf_module)
|
||||
url_obj = url(pattern, include(urlconf_module))
|
||||
urls_list.append(url_obj)
|
||||
return urls_list
|
||||
@@ -1,10 +1,10 @@
|
||||
import argparse
|
||||
import importlib
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .config import DJANGO_SETTINGS_DIR, DeployedAppsConfig
|
||||
from .exceptions import FatalError
|
||||
from .api import DjangoProject
|
||||
from .exceptions import DjangoDeployError
|
||||
from .version import VERSION
|
||||
|
||||
|
||||
@@ -14,18 +14,29 @@ class Program(object): # pylint: disable=too-few-public-methods
|
||||
parser.add_argument('-V', '--version', action='version',
|
||||
version='%(prog)s ' + VERSION)
|
||||
|
||||
parser.add_argument('-a', '--app',
|
||||
action='append', dest='merge_apps', metavar='MODULE',
|
||||
help='Merge settings from the django app MODULE\n'
|
||||
'Can be used multiple times')
|
||||
|
||||
parser.add_argument('-c', '--create',
|
||||
action='store_true', dest='create',
|
||||
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('--no-install-hooks',
|
||||
action='store_true', dest='no_install_hooks',
|
||||
help='Do not install django deploy hooks while creating django project directory')
|
||||
|
||||
parser.add_argument('--install-hooks',
|
||||
action='store_true', dest='install_hooks',
|
||||
help='Install django deploy hooks into existing django project directory')
|
||||
|
||||
parser.add_argument('-a', '--install-app',
|
||||
action='append', dest='install_apps', metavar='MODULE',
|
||||
help='Merge settings from django app MODULE into django project settings'
|
||||
'. Can be used multiple times')
|
||||
|
||||
parser.add_argument('-m', '--mount-app',
|
||||
nargs=2,
|
||||
action='append', dest='mount_apps', metavar=('MODULE', 'ROUTE'),
|
||||
help='Merge settings from django app MODULE into django project settings'
|
||||
' and add the app root urlconf to django root urlconf as ROUTE'
|
||||
'. Can be used multiple times')
|
||||
|
||||
parser.add_argument('project_dir', metavar='PATH',
|
||||
help='The directory, where the django project is or will be installed.')
|
||||
@@ -41,111 +52,64 @@ class Program(object): # pylint: disable=too-few-public-methods
|
||||
return self._argparser.parse_args(argv)
|
||||
|
||||
@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 _create_django_project(project_dir, install_hooks=True):
|
||||
project = DjangoProject(project_dir)
|
||||
project.create(install_hooks=install_hooks)
|
||||
|
||||
@staticmethod
|
||||
def _install_django_files(project_dir, overwrite=False):
|
||||
if not os.path.exists(project_dir):
|
||||
os.makedirs(project_dir)
|
||||
|
||||
settings_dir = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||
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)
|
||||
sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir))
|
||||
exitval = os.system(cmd)
|
||||
if exitval != os.EX_OK: # pragma: no cover
|
||||
raise FatalError(exitval=exitval)
|
||||
def _install_hooks(project_dir):
|
||||
project = DjangoProject(project_dir)
|
||||
project.install_hooks()
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
def _install_app(project_dir, module_path):
|
||||
project = DjangoProject(project_dir)
|
||||
project.install_app(module_path)
|
||||
|
||||
@staticmethod
|
||||
def _add_installed_apps_from_app(project_dir, app):
|
||||
settings_from_app = importlib.import_module('{}.django_settings'.format(app))
|
||||
wanted_apps = getattr(settings_from_app, 'ADD_INSTALLED_APPS', [])
|
||||
|
||||
config = DeployedAppsConfig(project_dir=project_dir)
|
||||
if app not in config:
|
||||
config[app] = {}
|
||||
|
||||
config[app]['INSTALLED_APPS'] = wanted_apps
|
||||
config.write()
|
||||
|
||||
@staticmethod
|
||||
def _add_urlpatterns_from_app(project_dir, app):
|
||||
settings_from_app = importlib.import_module('{}.django_settings'.format(app))
|
||||
wanted_urls = getattr(settings_from_app, 'ADD_URLPATTERNS', [])
|
||||
|
||||
config = DeployedAppsConfig(project_dir=project_dir)
|
||||
if app not in config: # pragma: no cover
|
||||
config[app] = {}
|
||||
|
||||
config[app]['urlpatterns'] = wanted_urls
|
||||
config.write()
|
||||
|
||||
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 _mount_app(project_dir, module_path, route):
|
||||
project = DjangoProject(project_dir)
|
||||
project.mount_app(module_path, route)
|
||||
|
||||
def __init__(self):
|
||||
self._argparser = argparse.ArgumentParser()
|
||||
self._setup_argparser(self._argparser)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
def __call__(self, *args, **kwargs): # pylint: disable=too-many-branches
|
||||
argv = kwargs.get('argv', None)
|
||||
cmd_args = self._parse_args(argv)
|
||||
exitval = os.EX_OK
|
||||
exceptions = []
|
||||
try:
|
||||
if cmd_args.create:
|
||||
self._install_django_files(cmd_args.project_dir)
|
||||
if cmd_args.create or cmd_args.enable:
|
||||
self._install_django_deploy_files(cmd_args.project_dir)
|
||||
self._enable_django_deploy(cmd_args.project_dir)
|
||||
if cmd_args.merge_apps:
|
||||
for app in cmd_args.merge_apps:
|
||||
self._merge_app(cmd_args.project_dir, app)
|
||||
except FatalError as e:
|
||||
exitval = e.exitval
|
||||
self._create_django_project(cmd_args.project_dir, install_hooks=not cmd_args.no_install_hooks)
|
||||
if cmd_args.install_hooks:
|
||||
self._install_hooks(cmd_args.project_dir)
|
||||
if cmd_args.install_apps:
|
||||
for app in cmd_args.install_apps:
|
||||
try:
|
||||
self._install_app(cmd_args.project_dir, app)
|
||||
except DjangoDeployError as e:
|
||||
if e.code in (errno.ENOPKG, errno.ENOENT):
|
||||
exceptions.append(e)
|
||||
continue
|
||||
raise # pragma: no cover
|
||||
if cmd_args.mount_apps:
|
||||
for app, route in cmd_args.mount_apps:
|
||||
try:
|
||||
self._mount_app(cmd_args.project_dir, app, route)
|
||||
except DjangoDeployError as e:
|
||||
if e.code in (errno.ENOPKG, errno.ENOENT):
|
||||
exceptions.append(e)
|
||||
continue
|
||||
raise # pragma: no cover
|
||||
except DjangoDeployError as e:
|
||||
exceptions.append(e)
|
||||
|
||||
for e in exceptions:
|
||||
if e.message:
|
||||
sys.stderr.write('{}\n'.format(e.message))
|
||||
elif e.code: # pragma: no cover
|
||||
sys.stderr.write('{}\n'.format(os.strerror(e.code)))
|
||||
exitval = e.exitval or os.EX_SOFTWARE
|
||||
return exitval
|
||||
|
||||
137
src/django_deploy/tests/base.py
Normal file
137
src/django_deploy/tests/base.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from ..config import DJANGO_SETTINGS_DIR
|
||||
|
||||
|
||||
class DjangoDeployTestCase(unittest.TestCase):
|
||||
@pytest.fixture(autouse=True)
|
||||
def tmpdir(self, tmpdir): # pylint: disable=method-hidden
|
||||
self.tmpdir = tmpdir
|
||||
|
||||
@staticmethod
|
||||
def _import_from_dir(module_name, directory):
|
||||
sys.path.insert(0, directory)
|
||||
module = importlib.import_module(module_name)
|
||||
if sys.version_info.major == 2: # pragma: no cover
|
||||
reload(module) # pylint: disable=undefined-variable
|
||||
else: # pragma: no cover
|
||||
importlib.reload(module) # pylint: disable=no-member
|
||||
sys.path.pop(0)
|
||||
return module
|
||||
|
||||
def get_django_settings(self, project_dir):
|
||||
module_name = 'settings'
|
||||
directory = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||
return self._import_from_dir(module_name, directory)
|
||||
|
||||
def get_django_root_urlconf(self, project_dir):
|
||||
module_name = 'urls'
|
||||
directory = os.path.join(project_dir, DJANGO_SETTINGS_DIR)
|
||||
sys.modules['django.contrib'] = mock.MagicMock(name='django.contrib')
|
||||
return self._import_from_dir(module_name, directory)
|
||||
|
||||
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_hooks(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 assert_no_django_deploy_hooks(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.assertFalse(os.path.isfile(django_deploy_json))
|
||||
|
||||
with open(settings_file, 'r') as f:
|
||||
needle = 'from django_deploy import get_installed_apps'
|
||||
haystack = f.read()
|
||||
self.assertNotIn(needle, haystack)
|
||||
|
||||
with open(urlconf_file, 'r') as f:
|
||||
needle = 'from django_deploy import get_urlpatterns'
|
||||
haystack = f.read()
|
||||
self.assertNotIn(needle, haystack)
|
||||
|
||||
def assert_installed_apps(self, project_dir, apps, default_apps=None):
|
||||
settings = self.get_django_settings(project_dir)
|
||||
|
||||
if default_apps is None:
|
||||
expected_apps = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
else: # pragma: no cover
|
||||
expected_apps = default_apps
|
||||
|
||||
expected_apps += apps
|
||||
|
||||
self.assertListEqual(expected_apps, settings.INSTALLED_APPS)
|
||||
|
||||
def assert_urlpatterns(self, project_dir, patterns):
|
||||
root_urlconf = self.get_django_root_urlconf(project_dir)
|
||||
|
||||
# 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 += patterns
|
||||
|
||||
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))
|
||||
@@ -0,0 +1 @@
|
||||
from . import django_deploy_hooks
|
||||
|
||||
2
src/django_deploy/tests/fake_app1/django_deploy_hooks.py
Normal file
2
src/django_deploy/tests/fake_app1/django_deploy_hooks.py
Normal file
@@ -0,0 +1,2 @@
|
||||
INSTALLED_APPS = ['django_deploy.tests.fake_app1']
|
||||
ROOT_URLCONF = '.urls'
|
||||
@@ -1,5 +0,0 @@
|
||||
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/'},
|
||||
]
|
||||
2
src/django_deploy/tests/fake_app2/django_deploy_hooks.py
Normal file
2
src/django_deploy/tests/fake_app2/django_deploy_hooks.py
Normal file
@@ -0,0 +1,2 @@
|
||||
INSTALLED_APPS = ['django_deploy.tests.fake_app1', 'django_deploy.tests.fake_app2']
|
||||
ROOT_URLCONF = 'django_deploy.tests.fake_app2.urls'
|
||||
@@ -1,5 +0,0 @@
|
||||
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'},
|
||||
]
|
||||
204
src/django_deploy/tests/test_api.py
Normal file
204
src/django_deploy/tests/test_api.py
Normal file
@@ -0,0 +1,204 @@
|
||||
import errno
|
||||
import os
|
||||
|
||||
from ..api import DjangoProjectHooksConfig, DjangoProject
|
||||
from ..exceptions import DjangoDeployError
|
||||
|
||||
from .base import DjangoDeployTestCase
|
||||
|
||||
|
||||
class DjangoProjectHooksConfigTestCase(DjangoDeployTestCase):
|
||||
def test_init(self):
|
||||
with self.assertRaises(AssertionError):
|
||||
DjangoProjectHooksConfig()
|
||||
with self.assertRaises(AssertionError):
|
||||
DjangoProjectHooksConfig(project_dir='.', settings_dir='.')
|
||||
|
||||
|
||||
class DjangoProjectTestCase(DjangoDeployTestCase):
|
||||
def setUp(self):
|
||||
self._tmp_dir = str(self.tmpdir)
|
||||
|
||||
def test_init(self):
|
||||
with self.assertRaises(TypeError):
|
||||
DjangoProject() # pylint: disable=no-value-for-parameter
|
||||
# existing project dir
|
||||
DjangoProject(self._tmp_dir)
|
||||
# not existing project dir
|
||||
project_dir = os.path.join(self._tmp_dir, 'new')
|
||||
DjangoProject(project_dir)
|
||||
# project dir is an existing file
|
||||
os.mknod(project_dir)
|
||||
DjangoProject(project_dir)
|
||||
|
||||
def test_create(self):
|
||||
# the parent of the to-be-created project dir shall also not exist.
|
||||
project_dir = os.path.join(self._tmp_dir, 'new', 'sub', 'sub')
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_create_in_empty_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_create_in_project_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
project = DjangoProject(project_dir)
|
||||
|
||||
try:
|
||||
project.create()
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.EEXIST, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_create_without_installing_hooks(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create(install_hooks=False)
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_no_django_deploy_hooks(project_dir)
|
||||
|
||||
try:
|
||||
project.create(install_hooks=False)
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.EEXIST, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
try:
|
||||
project.create()
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.EEXIST, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_install_hooks(self):
|
||||
project_dir = os.path.join(self._tmp_dir, 'new')
|
||||
project = DjangoProject(project_dir)
|
||||
try:
|
||||
project.install_hooks()
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.ENOENT, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_install_hooks_in_empty_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
try:
|
||||
project.install_hooks()
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.EISDIR, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_install_hooks_in_project_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create(install_hooks=False)
|
||||
self.assert_django_project(project_dir)
|
||||
project.install_hooks()
|
||||
self.assert_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_install_hooks_after_create(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
try:
|
||||
project.install_hooks()
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.EEXIST, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_install_app(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
project.install_app('django_deploy.tests.fake_app1')
|
||||
|
||||
installed_apps = [
|
||||
'django_deploy.tests.fake_app1',
|
||||
]
|
||||
self.assert_installed_apps(project_dir, installed_apps)
|
||||
|
||||
def test_install_apps(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
project.install_app('django_deploy.tests.fake_app1')
|
||||
project.install_app('django_deploy.tests.fake_app1')
|
||||
project.install_app('django_deploy.tests.fake_app2')
|
||||
|
||||
installed_apps = [
|
||||
'django_deploy.tests.fake_app1',
|
||||
'django_deploy.tests.fake_app2',
|
||||
]
|
||||
self.assert_installed_apps(project_dir, installed_apps)
|
||||
|
||||
def test_install_nonexisting_app(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
try:
|
||||
project.install_app('django_deploy.tests.fake_app0')
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.ENOPKG, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_install_nonconforming_app(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
try:
|
||||
project.install_app('django_deploy.tests.fake_app1', hooks_config_name='non_existing_submodule')
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.ENOENT, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
|
||||
def test_mount_app(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
project.mount_app('django_deploy.tests.fake_app1', '')
|
||||
|
||||
expected_urlpatterns = [
|
||||
('URLResolver', '^/', 'django_deploy.tests.fake_app1.urls'),
|
||||
]
|
||||
self.assert_urlpatterns(project_dir, expected_urlpatterns)
|
||||
|
||||
def test_mount_apps(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
project.mount_app('django_deploy.tests.fake_app1', 'app1')
|
||||
project.mount_app('django_deploy.tests.fake_app1', 'app1')
|
||||
project.mount_app('django_deploy.tests.fake_app2', 'app2')
|
||||
|
||||
expected_urlpatterns = [
|
||||
('URLResolver', '^app1/', 'django_deploy.tests.fake_app1.urls'),
|
||||
('URLResolver', '^app2/', 'django_deploy.tests.fake_app2.urls'),
|
||||
]
|
||||
self.assert_urlpatterns(project_dir, expected_urlpatterns)
|
||||
|
||||
def test_mount_unmountable_app(self):
|
||||
project_dir = self._tmp_dir
|
||||
project = DjangoProject(project_dir)
|
||||
project.create()
|
||||
|
||||
try:
|
||||
project.mount_app('django_deploy', '/')
|
||||
except DjangoDeployError as e:
|
||||
self.assertEqual(errno.ENOLINK, e.code)
|
||||
else: # pragma: no cover
|
||||
self.fail('DjangoDeployError not raised')
|
||||
40
src/django_deploy/tests/test_base_types.py
Normal file
40
src/django_deploy/tests/test_base_types.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import unittest
|
||||
|
||||
from ..base_types import OrderedDict
|
||||
|
||||
|
||||
class OrderedDictTestCase(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _create_filled_object(items):
|
||||
obj = OrderedDict()
|
||||
for key, value in items:
|
||||
obj[key] = value
|
||||
return obj
|
||||
|
||||
def setUp(self):
|
||||
self._test_data = [
|
||||
('a', 'AA'),
|
||||
('c', 'CC'),
|
||||
('b', 'BB'),
|
||||
]
|
||||
self._obj = self._create_filled_object(self._test_data)
|
||||
|
||||
def test_contain(self):
|
||||
self.assertIn('a', self._obj)
|
||||
self.assertIn('b', self._obj)
|
||||
self.assertIn('c', self._obj)
|
||||
self.assertNotIn('d', self._obj)
|
||||
|
||||
def test_len(self):
|
||||
self.assertEqual(3, len(self._obj))
|
||||
|
||||
def test_del(self):
|
||||
del self._obj['c']
|
||||
self.assertEqual(2, len(self._obj))
|
||||
|
||||
def test_iter(self):
|
||||
items = []
|
||||
for key in self._obj:
|
||||
items.append((key, self._obj[key]))
|
||||
|
||||
self.assertListEqual(self._test_data, items)
|
||||
@@ -1,11 +1,10 @@
|
||||
import unittest
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from ..config import get_installed_apps, get_urlpatterns, DeployedAppsConfig
|
||||
from ..hooks import get_installed_apps, get_urlpatterns
|
||||
|
||||
|
||||
class FunctionsTestCase(unittest.TestCase):
|
||||
class HooksTestCase(unittest.TestCase):
|
||||
def test_get_installed_apps(self):
|
||||
test_data_sets = [
|
||||
(__file__, ['fake_app1', 'fake_app2']),
|
||||
@@ -29,26 +28,3 @@ class FunctionsTestCase(unittest.TestCase):
|
||||
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))
|
||||
@@ -1,173 +1,141 @@
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from ..config import DJANGO_SETTINGS_DIR
|
||||
from ..program import Program
|
||||
|
||||
from .base import DjangoDeployTestCase
|
||||
|
||||
class ProgramTestCase(unittest.TestCase):
|
||||
@pytest.fixture(autouse=True)
|
||||
def tmpdir(self, tmpdir): # pylint: disable=method-hidden
|
||||
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)
|
||||
|
||||
class ProgramTestCase(DjangoDeployTestCase):
|
||||
def setUp(self):
|
||||
self._program = Program()
|
||||
self._project_dir = os.path.join(str(self.tmpdir), 'django')
|
||||
self._program(argv=['--create', self._project_dir])
|
||||
self._tmp_dir = str(self.tmpdir)
|
||||
|
||||
def test_create_new_dir(self):
|
||||
project_dir = os.path.join(str(self.tmpdir), 'new_dir')
|
||||
def test_create(self):
|
||||
project_dir = os.path.join(self._tmp_dir, 'new')
|
||||
exitval = self._program(argv=['--create', project_dir])
|
||||
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_create_in_empty_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
exitval = self._program(argv=['-c', project_dir])
|
||||
|
||||
self.assertEqual(os.EX_OK, exitval, 'program() does not return os.EX_OK')
|
||||
self._assert_django_project(project_dir)
|
||||
self._assert_django_deploy_project(project_dir)
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_create_existing_empty_dir(self):
|
||||
project_dir = os.path.join(str(self.tmpdir), 'empty_dir')
|
||||
os.makedirs(project_dir)
|
||||
def test_create_in_project_dir(self):
|
||||
project_dir = self._tmp_dir
|
||||
self._program(argv=['-c', project_dir])
|
||||
exitval = self._program(argv=['-c', project_dir])
|
||||
self.assertNotEqual(os.EX_OK, exitval)
|
||||
|
||||
self.assertEqual(os.EX_OK, exitval, 'program() does not return os.EX_OK')
|
||||
self._assert_django_project(project_dir)
|
||||
self._assert_django_deploy_project(project_dir)
|
||||
def test_create_without_hooks(self):
|
||||
project_dir = self._tmp_dir
|
||||
exitval = self._program(argv=['-c', '--no-install-hooks', project_dir])
|
||||
|
||||
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')
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_no_django_deploy_hooks(project_dir)
|
||||
|
||||
def test_enable_django_deploy(self):
|
||||
project_dir = os.path.join(str(self.tmpdir), 'pure_django')
|
||||
os.makedirs(project_dir)
|
||||
def test_install_hooks_without_project_dir(self):
|
||||
project_dir = os.path.join(self._tmp_dir, 'new')
|
||||
exitval = self._program(argv=['--install-hooks', project_dir])
|
||||
self.assertNotEqual(os.EX_OK, exitval)
|
||||
|
||||
project_dir = os.path.join(self._tmp_dir)
|
||||
exitval = self._program(argv=['--install-hooks', project_dir])
|
||||
self.assertNotEqual(os.EX_OK, exitval)
|
||||
|
||||
def test_install_hooks_in_pure_django(self):
|
||||
project_dir = self._tmp_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)
|
||||
exitval = self._program(argv=['--install-hooks', project_dir])
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
self.assert_django_project(project_dir)
|
||||
self.assert_django_deploy_hooks(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):
|
||||
project_dir = self._project_dir
|
||||
def test_install_hooks_twice(self):
|
||||
project_dir = self._tmp_dir
|
||||
self._program(argv=['-c', project_dir])
|
||||
exitval = self._program(argv=['--install-hooks', project_dir])
|
||||
self.assertEqual(os.EX_SOFTWARE, exitval)
|
||||
|
||||
def test_install_apps(self):
|
||||
project_dir = self._tmp_dir
|
||||
argv = [
|
||||
'-a', 'django_deploy',
|
||||
'-a', 'django_deploy.tests.fake_app1',
|
||||
'-c',
|
||||
'--install-app', 'django_deploy.tests.fake_app1',
|
||||
'--install-app', 'django_deploy.tests.fake_app1',
|
||||
'-a', 'django_deploy.tests.fake_app2',
|
||||
project_dir]
|
||||
self._program(argv=argv)
|
||||
project_dir
|
||||
]
|
||||
exitval = self._program(argv=argv)
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
|
||||
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 = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
installed_apps = [
|
||||
'django_deploy.tests.fake_app1',
|
||||
'django_deploy.tests.fake_app2',
|
||||
]
|
||||
|
||||
self.assertListEqual(expected_installed_apps, settings.INSTALLED_APPS)
|
||||
|
||||
def test_merge_root_urlconf(self):
|
||||
project_dir = self._project_dir
|
||||
self.assert_installed_apps(project_dir, installed_apps)
|
||||
|
||||
def test_install_apps_with_error(self):
|
||||
project_dir = self._tmp_dir
|
||||
argv = [
|
||||
'-a', 'django_deploy',
|
||||
'-c',
|
||||
'-a', 'django_deploy.tests.fake_app1',
|
||||
'-a', 'django_deploy.tests.fake_app0',
|
||||
'-a', 'django_deploy.tests.fake_app2',
|
||||
project_dir]
|
||||
self._program(argv=argv)
|
||||
project_dir
|
||||
]
|
||||
exitval = self._program(argv=argv)
|
||||
self.assertNotEqual(os.EX_OK, exitval)
|
||||
|
||||
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'),
|
||||
installed_apps = [
|
||||
'django_deploy.tests.fake_app1',
|
||||
'django_deploy.tests.fake_app2',
|
||||
]
|
||||
|
||||
real_urlpatterns = root_urlconf.urlpatterns
|
||||
self.assertEqual(len(expected_urlpatterns), len(real_urlpatterns))
|
||||
self.assert_installed_apps(project_dir, installed_apps)
|
||||
|
||||
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))
|
||||
def test_mount_apps(self):
|
||||
project_dir = self._tmp_dir
|
||||
argv = [
|
||||
'-c',
|
||||
'--mount-app', 'django_deploy.tests.fake_app1', 'app1',
|
||||
'--mount-app', 'django_deploy.tests.fake_app1', 'app1',
|
||||
'-m', 'django_deploy.tests.fake_app2', 'app2',
|
||||
project_dir
|
||||
]
|
||||
exitval = self._program(argv=argv)
|
||||
self.assertEqual(os.EX_OK, exitval)
|
||||
|
||||
urlpatterns = [
|
||||
('URLResolver', '^app1/', 'django_deploy.tests.fake_app1.urls'),
|
||||
('URLResolver', '^app2/', 'django_deploy.tests.fake_app2.urls'),
|
||||
]
|
||||
self.assert_urlpatterns(project_dir, urlpatterns)
|
||||
|
||||
def test_mount_apps_with_errors(self):
|
||||
project_dir = self._tmp_dir
|
||||
self._program(argv=['-c', project_dir])
|
||||
argv = [
|
||||
'-m', 'django_deploy.tests.fake_app1', 'app1',
|
||||
'-m', 'django_deploy.tests.fake_app0', 'app0',
|
||||
'-m', 'django_deploy.tests.fake_app2', 'app2',
|
||||
project_dir
|
||||
]
|
||||
exitval = self._program(argv=argv)
|
||||
self.assertNotEqual(os.EX_OK, exitval)
|
||||
|
||||
patterns = [
|
||||
('URLResolver', '^app1/', 'django_deploy.tests.fake_app1.urls'),
|
||||
('URLResolver', '^app2/', 'django_deploy.tests.fake_app2.urls'),
|
||||
]
|
||||
self.assert_urlpatterns(project_dir, patterns)
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION = '0.1.dev0'
|
||||
VERSION = '0.2.dev0'
|
||||
|
||||
Reference in New Issue
Block a user