Source code for djangocms_installer.config

import argparse
import locale
import os.path
import sys
import warnings
from distutils.version import LooseVersion

import pytz

from .. import compat
from ..utils import less_than_version, supported_versions
from . import data, ini
from .internal import DbAction, validate_project


[docs]def parse(args): """ Define the available arguments """ from tzlocal import get_localzone try: timezone = get_localzone() if isinstance(timezone, pytz.BaseTzInfo): timezone = timezone.zone except Exception: # pragma: no cover timezone = "UTC" if timezone == "local": timezone = "UTC" parser = argparse.ArgumentParser( description="""Bootstrap a django CMS project. Major usage modes: - wizard: djangocms -w -p /path/whatever project_name: ask for all the options through a CLI wizard. - batch: djangocms project_name: runs with the default values plus any additional option provided (see below) with no question asked. - config file: djangocms_installer --config-file /path/to/config.ini project_name: reads values from an ini-style config file. Check https://djangocms-installer.readthedocs.io/en/latest/usage.html for detailed usage information. """, formatter_class=argparse.RawTextHelpFormatter, ) parser.add_argument( "--config-file", dest="config_file", action="store", default=None, help="Configuration file for djangocms_installer", ) parser.add_argument( "--config-dump", dest="config_dump", action="store", default=None, help="Dump configuration file with current args", ) parser.add_argument( "--db", "-d", dest="db", action=DbAction, default="sqlite://localhost/project.db", help="Database configuration (in URL format). " "Example: sqlite://localhost/project.db", ) parser.add_argument( "--i18n", "-i", dest="i18n", action="store", choices=("yes", "no"), default="yes", help="Activate Django I18N / L10N setting; this is " "automatically activated if more than " "language is provided", ) parser.add_argument( "--use-tz", "-z", dest="use_timezone", action="store", choices=("yes", "no"), default="yes", help="Activate Django timezone support", ) parser.add_argument( "--timezone", "-t", dest="timezone", required=False, default=timezone, action="store", help="Optional default time zone. Example: Europe/Rome", ) parser.add_argument( "--reversion", "-e", dest="reversion", action="store", choices=("yes", "no"), default="yes", help="Install and configure reversion support " "(only for django CMS 3.2 and 3.3)", ) parser.add_argument( "--permissions", dest="permissions", action="store", choices=("yes", "no"), default="no", help="Activate CMS permission management", ) parser.add_argument("--pip-options", help="pass custom pip options", default="") parser.add_argument( "--languages", "-l", dest="languages", action="append", help="Languages to enable. Option can be provided multiple times, or as a " "comma separated list. Only language codes supported by Django can " "be used here. Example: en, fr-FR, it-IT", ) parser.add_argument( "--django-version", dest="django_version", action="store", choices=data.DJANGO_SUPPORTED, default=data.DJANGO_DEFAULT, help="Django version", ) parser.add_argument( "--cms-version", "-v", dest="cms_version", action="store", choices=data.DJANGOCMS_SUPPORTED, default=data.DJANGOCMS_DEFAULT, help="django CMS version", ) parser.add_argument( "--parent-dir", "-p", dest="project_directory", default="", action="store", help="Optional project parent directory", ) parser.add_argument( "--bootstrap", dest="bootstrap", action="store", choices=("yes", "no"), default="no", help="Use Bootstrap 4 Theme", ) parser.add_argument( "--templates", dest="templates", action="store", default="no", help="Use custom template set", ) parser.add_argument( "--starting-page", dest="starting_page", action="store", choices=("yes", "no"), default="no", help="Load a starting page with examples after installation " '(english language only). Choose "no" if you use a ' "custom template set.", ) parser.add_argument(dest="project_name", action="store", help="Name of the project to be created") # Command that lists the supported plugins in verbose description parser.add_argument( "--list-plugins", "-P", dest="plugins", action="store_true", help="List plugins that's going to be installed and configured", ) # Command that lists the supported plugins in verbose description parser.add_argument( "--dump-requirements", "-R", dest="dump_reqs", action="store_true", help="It dumps the requirements that would be installed according to " "parameters given. Together with --requirements argument is useful " "for customizing the virtualenv", ) # Advanced options. These have a predefined default and are not asked # by config wizard. parser.add_argument( "--no-input", "-q", dest="noinput", action="store_true", default=True, help="Don't run the configuration wizard, just use the " "provided values", ) parser.add_argument( "--wizard", "-w", dest="wizard", action="store_true", default=False, help="Run the configuration wizard", ) parser.add_argument( "--verbose", dest="verbose", action="store_true", default=False, help="Be more verbose and don't swallow subcommands output", ) parser.add_argument( "--filer", "-f", dest="filer", action="store_true", default=True, help="Install and configure django-filer plugins " "- Always enabled", ) parser.add_argument( "--requirements", "-r", dest="requirements_file", action="store", default=None, help="Externally defined requirements file", ) parser.add_argument( "--no-deps", "-n", dest="no_deps", action="store_true", default=False, help="Don't install package dependencies", ) parser.add_argument( "--no-plugins", dest="no_plugins", action="store_true", default=False, help="Don't install plugins", ) parser.add_argument( "--no-db-driver", dest="no_db_driver", action="store_true", default=False, help="Don't install database package", ) parser.add_argument( "--no-sync", "-m", dest="no_sync", action="store_true", default=False, help="Don't run syncdb / migrate after bootstrapping", ) parser.add_argument( "--no-user", "-u", dest="no_user", action="store_true", default=False, help="Don't create the admin user", ) parser.add_argument( "--template", dest="template", action="store", default=None, help="The path or URL to load the django project " "template from.", ) parser.add_argument( "--extra-settings", dest="extra_settings", action="store", default=None, help="The path to an file that contains extra settings.", ) parser.add_argument( "--skip-empty-check", "-s", dest="skip_project_dir_check", action="store_true", default=False, help="Skip the check if project dir is empty.", ) parser.add_argument( "--delete-project-dir", "-c", dest="delete_project_dir", action="store_true", default=False, help="Delete project directory on creation failure.", ) parser.add_argument( "--utc", dest="utc", action="store_true", default=False, help="Use UTC timezone.", ) if "--utc" in args: for action in parser._positionals._actions: if action.dest == "timezone": action.default = "UTC" # If config_args then pretend that config args came from the stdin and run parser again. config_args = ini.parse_config_file(parser, args) args = parser.parse_args(config_args + args) if not args.wizard: args.noinput = True else: args.noinput = False if not args.project_directory: args.project_directory = args.project_name args.project_directory = os.path.abspath(args.project_directory) # First of all, check if the project name is valid if not validate_project(args.project_name): sys.stderr.write( 'Project name "{}" is not valid or it\'s already defined. ' "Please use only numbers, letters and underscores.\n".format(args.project_name) ) sys.exit(3) # Checking the given path args.project_path = os.path.join(args.project_directory, args.project_name).strip() if not args.skip_project_dir_check: if os.path.exists(args.project_directory) and [ path for path in os.listdir(args.project_directory) if not path.startswith(".") ]: sys.stderr.write( 'Path "{}" already exists and is not empty, please choose a different one\n' "If you want to use this path anyway use the -s flag to skip this check.\n" "".format(args.project_directory) ) sys.exit(4) if os.path.exists(args.project_path): sys.stderr.write('Path "{}" already exists, please choose a different one\n'.format(args.project_path)) sys.exit(4) if args.config_dump and os.path.isfile(args.config_dump): sys.stdout.write('Cannot dump because given configuration file "{}" exists.\n'.format(args.config_dump)) sys.exit(8) args = _manage_args(parser, args) # what do we want here?! # * if languages are given as multiple arguments, let's use it as is # * if no languages are given, use a default and stop handling it further # * if languages are given as a comma-separated list, split it and use the # resulting list. if not args.languages: try: args.languages = [locale.getdefaultlocale()[0].split("_")[0]] except Exception: # pragma: no cover args.languages = ["en"] elif isinstance(args.languages, str): args.languages = args.languages.split(",") elif len(args.languages) == 1 and isinstance(args.languages[0], str): args.languages = args.languages[0].split(",") args.languages = [lang.strip().lower() for lang in args.languages] if len(args.languages) > 1: args.i18n = "yes" args.filer = True # Convert version to numeric format for easier checking try: django_version, cms_version = supported_versions(args.django_version, args.cms_version) cms_package = data.PACKAGE_MATRIX.get(cms_version, data.PACKAGE_MATRIX[data.DJANGOCMS_LTS]) except RuntimeError as e: # pragma: no cover sys.stderr.write(str(e)) sys.exit(6) if django_version is None: # pragma: no cover sys.stderr.write( "Please provide a Django supported version: {}. Only Major.Minor " "version selector is accepted\n".format(", ".join(data.DJANGO_SUPPORTED)) ) sys.exit(6) if cms_version is None: # pragma: no cover sys.stderr.write( "Please provide a django CMS supported version: {}. Only Major.Minor " "version selector is accepted\n".format(", ".join(data.DJANGOCMS_SUPPORTED)) ) sys.exit(6) default_settings = "{}.settings".format(args.project_name) env_settings = os.environ.get("DJANGO_SETTINGS_MODULE", default_settings) if env_settings != default_settings: sys.stderr.write( "`DJANGO_SETTINGS_MODULE` is currently set to '{}' which is not compatible with " "djangocms installer.\nPlease unset `DJANGO_SETTINGS_MODULE` and re-run the installer " "\n".format(env_settings) ) sys.exit(10) if not args.requirements_file: requirements = [] # django CMS version check if args.cms_version == "develop": requirements.append(cms_package) warnings.warn(data.VERSION_WARNING.format("develop", "django CMS")) elif args.cms_version == "rc": # pragma: no cover requirements.append(cms_package) elif args.cms_version == "beta": # pragma: no cover requirements.append(cms_package) warnings.warn(data.VERSION_WARNING.format("beta", "django CMS")) else: requirements.append(cms_package) if args.cms_version in ("rc", "develop"): requirements.extend(data.REQUIREMENTS["cms-master"]) elif LooseVersion(cms_version) >= LooseVersion("3.7"): requirements.extend(data.REQUIREMENTS["cms-3.7"]) if not args.no_db_driver: requirements.append(args.db_driver) if not args.no_plugins: if args.cms_version in ("rc", "develop"): requirements.extend(data.REQUIREMENTS["plugins-master"]) elif LooseVersion(cms_version) >= LooseVersion("3.7"): requirements.extend(data.REQUIREMENTS["plugins-3.7"]) requirements.extend(data.REQUIREMENTS["filer"]) # Django version check if args.django_version == "develop": # pragma: no cover requirements.append(data.DJANGO_DEVELOP) warnings.warn(data.VERSION_WARNING.format("develop", "Django")) elif args.django_version == "beta": # pragma: no cover requirements.append(data.DJANGO_BETA) warnings.warn(data.VERSION_WARNING.format("beta", "Django")) else: requirements.append("Django<{}".format(less_than_version(django_version))) if django_version == "2.2": requirements.extend(data.REQUIREMENTS["django-2.2"]) elif django_version == "3.0": requirements.extend(data.REQUIREMENTS["django-3.0"]) elif django_version == "3.1": requirements.extend(data.REQUIREMENTS["django-3.1"]) requirements.extend(data.REQUIREMENTS["default"]) args.requirements = "\n".join(requirements).strip() # Convenient shortcuts args.cms_version = cms_version args.django_version = django_version args.settings_path = os.path.join(args.project_directory, args.project_name, "settings.py").strip() args.urlconf_path = os.path.join(args.project_directory, args.project_name, "urls.py").strip() if args.config_dump: ini.dump_config_file(args.config_dump, args, parser) return args
[docs]def get_settings(): module = __import__("djangocms_installer.config", globals(), locals(), ["settings"]) return module.settings
[docs]def write_default(config): pass
[docs]def show_plugins(): """ Shows a descriptive text about supported plugins """ sys.stdout.write(str(data.PLUGIN_LIST_TEXT))
[docs]def show_requirements(args): """ Prints the list of requirements according to the arguments provided """ sys.stdout.write(str(args.requirements))
def _manage_args(parser, args): """ Checks and validate provided input """ for item in data.CONFIGURABLE_OPTIONS: action = parser._option_string_actions[item] choices = default = "" input_value = getattr(args, action.dest) new_val = None if not args.noinput: if action.choices: choices = " (choices: {})".format(", ".join(action.choices)) if input_value: if type(input_value) == list: default = " [default {}]".format(", ".join(input_value)) else: default = " [default {}]".format(input_value) while not new_val: prompt = "{}{}{}: ".format(action.help, choices, default) new_val = input(prompt) new_val = compat.clean(new_val) if not new_val and input_value: new_val = input_value if new_val and action.dest == "templates": if new_val != "no" and not os.path.isdir(new_val): sys.stdout.write("Given directory does not exists, retry\n") new_val = False if new_val and action.dest == "db": action(parser, args, new_val, action.option_strings) new_val = getattr(args, action.dest) else: if not input_value and action.required: # pragma: no cover raise ValueError("Option {} is required when in no-input mode".format(action.dest)) new_val = input_value if action.dest == "db": action(parser, args, new_val, action.option_strings) new_val = getattr(args, action.dest) if action.dest == "templates" and (new_val == "no" or not os.path.isdir(new_val)): new_val = False if action.dest in ("bootstrap", "starting_page"): new_val = new_val is True or new_val == "yes" setattr(args, action.dest, new_val) return args