208 lines
8.4 KiB
Python
208 lines
8.4 KiB
Python
import argparse
|
|
import datetime
|
|
import importlib
|
|
import os
|
|
import sys
|
|
|
|
from .config import DJANGO_SETTINGS_DIR
|
|
from .utils import get_root_urlconf
|
|
from .version import VERSION
|
|
|
|
|
|
class Program(object): # pylint: disable=too-few-public-methods
|
|
@staticmethod
|
|
def _setup_argparser(parser):
|
|
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('project_dir', metavar='PATH',
|
|
help='The directory, where the django project is or will be installed.')
|
|
|
|
return parser
|
|
|
|
def _parse_args(self, argv=None):
|
|
if argv is None:
|
|
argv = sys.argv[1:]
|
|
if not argv:
|
|
argv = ()
|
|
|
|
return self._argparser.parse_args(argv)
|
|
|
|
@staticmethod
|
|
def _install_django_files(project_dir, overwrite=False):
|
|
if 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)
|
|
|
|
settings_module = DJANGO_SETTINGS_DIR
|
|
cmd = 'django-admin startproject {name} {path}'.format(name=settings_module,
|
|
path=project_dir)
|
|
sys.stdout.write('Installing django files to {path}\n'.format(path=project_dir))
|
|
exitval = os.system(cmd)
|
|
return exitval
|
|
|
|
@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'):
|
|
wanted_apps = settings_from_app.ADD_INSTALLED_APPS
|
|
else:
|
|
wanted_apps = []
|
|
|
|
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 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 to settings.INSTALLED_APPS:'
|
|
' {apps}\n'.format(app=app, apps=', '.join(missing_apps)))
|
|
code = 'INSTALLED_APPS += [\n'
|
|
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,
|
|
timestamp=timestamp)
|
|
|
|
self._append_to_settings(project_dir, code, comment)
|
|
else:
|
|
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): # 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'):
|
|
wanted_urls = {}
|
|
for wanted in settings_from_app.ADD_URLPATTERNS:
|
|
wanted_urls[wanted['pattern']] = wanted
|
|
wanted_patterns = wanted_urls.keys()
|
|
else:
|
|
wanted_urls = {}
|
|
wanted_patterns = []
|
|
|
|
if wanted_urls:
|
|
sys.stdout.write('{app}: wanted urlpatterns: {urls}\n'.format(app=app, urls=', '.join(wanted_patterns)))
|
|
else:
|
|
return os.EX_OK
|
|
|
|
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)
|
|
if exitval != os.EX_OK:
|
|
return exitval
|
|
exitval = self._add_urlpatterns_from_app(project_dir, app)
|
|
return exitval
|
|
|
|
def __init__(self):
|
|
self._argparser = argparse.ArgumentParser()
|
|
self._setup_argparser(self._argparser)
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
argv = kwargs.get('argv', None)
|
|
cmd_args = self._parse_args(argv)
|
|
exitval = os.EX_OK
|
|
if cmd_args.create:
|
|
exitval = self._install_django_files(cmd_args.project_dir)
|
|
if exitval != os.EX_OK:
|
|
return exitval
|
|
if cmd_args.merge_apps:
|
|
for app in cmd_args.merge_apps:
|
|
exitval = self._merge_app(cmd_args.project_dir, app)
|
|
if exitval != os.EX_OK:
|
|
return exitval
|
|
return exitval
|