def handle_template(self, template, subdir): """ Determines where the app or project templates are. Use moose.__path__[0] as the default because we don't know into which directory Moose has been installed. """ if template is None: return path.join(moose.__path__[0], 'conf', subdir) else: if template.startswith('file://'): template = template[7:] expanded_template = path.expanduser(template) expanded_template = path.normpath(expanded_template) if path.isdir(expanded_template): return expanded_template if self.is_url(template): # downloads the file and returns the path absolute_path = self.download(template) else: absolute_path = path.abspath(expanded_template) if path.exists(absolute_path): return self.extract(absolute_path) raise CommandError("couldn't handle %s template %s." % (self.app_or_project, template))
def handle(self, **options): app_name, target = options.pop('name'), options.pop('directory') self.validate_name(app_name, "app") # Check that the app_name cannot be imported. try: import_module(app_name) except ImportError: pass else: raise CommandError( "%r conflicts with the name of an existing Python module and " "cannot be used as an app name. Please try another name." % app_name) super(Command, self).handle('app', app_name, target, **options) # copys the config template to the application config_template = options.pop('configfile') if not config_template.endswith(settings.CONFIG_EXTENSION): config_template += settings.CONFIG_EXTENSION project_name = os.path.basename(os.getcwd()) project_template_path = os.path.join(os.getcwd(), project_name, config_template) app_template_path = os.path.join(os.getcwd(), app_name, settings.CONF_TEMPLATE_NAME) shutil.copyfile(project_template_path, app_template_path)
def handle_action(self, app_config, action_alias, configs): # get user-defined class for the action action_klass = app_config.get_action_klass(action_alias) if not action_klass: raise CommandError("Unknown action alias '%s'." % action_alias) output = [] if configs == None: actor = action_klass(app_config, stdout=self.stdout, stderr=self.stderr, style=self.style) # when option `-c` was None, run without arguments output.append(actor.run()) else: # run with configs and get the output for conf_desc in configs: # Initialize the class each time to make it reentrancy actor = action_klass(app_config, stdout=self.stdout, stderr=self.stderr, style=self.style) config_loader = find_matched_conf(app_config, conf_desc) if not config_loader: # Instead of raising an exception, makes a report after all done err_msg = "No matched config found for description '%s', aborted." % conf_desc output.append(err_msg) continue else: config = config_loader._parse() output.append(actor.run(config=config)) return '\n'.join(output)
def call_command(command_name, *args, **options): """ Calls the given command, with the given options and args/kwargs. This is the primary API you should use for calling specific commands. `name` may be a string or a command object. Using a string is preferred unless the command object is required for further processing or testing. Some examples: call_command('migrate') call_command('shell', plain=True) call_command('sqlmigrate', 'myapp') from django.core.management.commands import flush cmd = flush.Command() call_command(cmd, verbosity=0, interactive=False) # Do something with cmd ... """ if isinstance(command_name, BaseCommand): # Command object passed in. command = command_name command_name = command.__class__.__module__.split('.')[-1] else: # Load the command object by name. try: app_name = get_commands()[command_name] except KeyError: raise CommandError("Unknown command: %r" % command_name) if isinstance(app_name, BaseCommand): # If the command is already loaded, use it directly. command = app_name else: command = load_command_class(app_name, command_name) # Simulate argument parsing to get the option defaults (see #10080 for details). parser = command.create_parser('', command_name) # Use the `dest` option name from the parser option opt_mapping = { sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest for s_opt in parser._actions if s_opt.option_strings } arg_options = { opt_mapping.get(key, key): value for key, value in options.items() } defaults = parser.parse_args(args=[force_text(a) for a in args]) defaults = dict(defaults._get_kwargs(), **arg_options) # Move positional args out of options to mimic legacy optparse args = defaults.pop('args', ()) if 'skip_checks' not in options: defaults['skip_checks'] = True return command.execute(*args, **defaults)
def validate_name(self, name, app_or_project): if name is None: raise CommandError( "you must provide %s %s name" % ("an" if app_or_project == "app" else "a", app_or_project)) # If it's not a valid directory name. if six.PY2: if not re.search(r'^[_a-zA-Z]\w*$', name): # Provide a smart error message, depending on the error. if not re.search(r'^[_a-zA-Z]', name): message = 'make sure the name begins with a letter or underscore' else: message = 'use only numbers, letters and underscores' raise CommandError("%r is not a valid %s name. Please %s." % (name, app_or_project, message)) else: if not name.isidentifier(): raise CommandError( "%r is not a valid %s name. Please make sure the name is " "a valid identifier." % (name, app_or_project))
def download(self, url): """ Downloads the given URL and returns the file name. """ def cleanup_url(url): tmp = url.rstrip('/') filename = tmp.split('/')[-1] if url.endswith('/'): display_url = tmp + '/' else: display_url = url return filename, display_url prefix = 'moose_%s_template_' % self.app_or_project tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_download') self.paths_to_remove.append(tempdir) filename, display_url = cleanup_url(url) if self.verbosity >= 2: self.stdout.write("Downloading %s\n" % display_url) try: the_path, info = urlretrieve(url, path.join(tempdir, filename)) except IOError as e: raise CommandError("couldn't download URL %s to %s: %s" % (url, filename, e)) used_name = the_path.split('/')[-1] # Trying to get better name from response headers content_disposition = info.get('content-disposition') if content_disposition: _, params = cgi.parse_header(content_disposition) guessed_filename = params.get('filename') or used_name else: guessed_filename = used_name # Falling back to content type guessing ext = self.splitext(guessed_filename)[1] content_type = info.get('content-type') if not ext and content_type: ext = mimetypes.guess_extension(content_type) if ext: guessed_filename += ext # Move the temporary file to a filename that has better # chances of being recognized by the archive utils if used_name != guessed_filename: guessed_path = path.join(tempdir, guessed_filename) shutil.move(the_path, guessed_path) return guessed_path # Giving up return the_path
def extract(self, filename): """ Extracts the given file to a temporarily and returns the path of the directory with the extracted content. """ prefix = 'moose_%s_template_' % self.app_or_project tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_extract') self.paths_to_remove.append(tempdir) if self.verbosity >= 2: self.stdout.write("Extracting %s\n" % filename) try: archive.extract(filename, tempdir) return tempdir except (archive.ArchiveException, IOError) as e: raise CommandError("couldn't extract file %s to %s: %s" % (filename, tempdir, e))
def handle_app_config(self, app_config, **options): self.action_alias = options['action'] self.configs = options['configs'] keep_quite = options['quite'] recipients = options['recipients'] message = options.get('message', self.comment()) # start to time record = CommandRecord(app_config, self, message) # get user-defined class for the action action_klass = app_config.get_action_klass(self.action_alias) if action_klass: actor = action_klass(app_config) else: raise CommandError("Unknown action alias '%s'." % self.action_alias) # run with configs and get the output output = [] for conf_desc in self.configs: config_loader = find_matched_conf(app_config, conf_desc) if not config_loader: # Instead of raising an exception, makes a report after all done err_msg = "No matched config found for description '%s', aborted." % conf_desc output.append(err_msg) continue else: config = config_loader._parse() output.append(actor.run(config=config, app=app_config)) # to close the timer record.done() if not keep_quite: records.add(record) output_str = '\n'.join(output) if recipients: mail_sender = CommandRunNotifier(recipients) context = mail_sender.get_context(record, self, output_str) mail_sender.send(context) return output_str
def handle(self, **options): project_name, target = options.pop('name'), options.pop('directory') self.validate_name(project_name, "project") # Check that the project_name cannot be imported. try: import_module(project_name) except ImportError: pass else: raise CommandError( "%r conflicts with the name of an existing Python module and " "cannot be used as a project name. Please try another name." % project_name) # Create a random SECRET_KEY to put it in the main settings. options['secret_key'] = get_random_secret_key() super(Command, self).handle('project', project_name, target, **options)
def handle(self, app_or_project, name, target=None, **options): self.app_or_project = app_or_project self.paths_to_remove = [] self.verbosity = options['verbosity'] self.validate_name(name, app_or_project) # if some directory is given, make sure it's nicely expanded if target is None: top_dir = path.join(os.getcwd(), name) try: os.makedirs(top_dir) except OSError as e: if e.errno == errno.EEXIST: message = "'%s' already exists" % top_dir else: message = e raise CommandError(message) else: top_dir = os.path.abspath(path.expanduser(target)) if not os.path.exists(top_dir): raise CommandError("Destination directory '%s' does not " "exist, please create it first." % top_dir) extensions = tuple(handle_extensions(options['extensions'])) extra_files = [] for file in options['files']: extra_files.extend(map(lambda x: x.strip(), file.split(','))) if self.verbosity >= 2: self.stdout.write("Rendering %s template files with " "extensions: %s\n" % (app_or_project, ', '.join(extensions))) self.stdout.write("Rendering %s template files with " "filenames: %s\n" % (app_or_project, ', '.join(extra_files))) base_name = '%s_name' % app_or_project base_subdir = '%s_template' % app_or_project base_directory = '%s_directory' % app_or_project camel_case_name = 'camel_case_%s_name' % app_or_project camel_case_value = ''.join(x for x in name.title() if x != '_') # context = Context(dict(options, **{ # base_name: name, # base_directory: top_dir, # camel_case_name: camel_case_value, # # 'docs_version': get_docs_version(), # 'moose_version': moose.__version__, # 'unicode_literals': '' if six.PY3 else '# -*- coding: utf-8 -*-\n' # 'from __future__ import unicode_literals\n\n', # }), autoescape=False) context = dict( options, **{ base_name: name, base_directory: top_dir, camel_case_name: camel_case_value, # 'docs_version': get_docs_version(), 'moose_version': moose.__version__, 'unicode_literals': '' if six.PY3 else '# -*- coding: utf-8 -*-\n' 'from __future__ import unicode_literals\n\n', }) # Setup a stub settings environment for template rendering if not settings.configured: settings.configure() if app_or_project == 'app': moose.setup() template_dir = self.handle_template(options['template'], base_subdir) prefix_length = len(template_dir) + 1 for root, dirs, files in os.walk(template_dir): path_rest = root[prefix_length:] relative_dir = path_rest.replace(base_name, name) if relative_dir: target_dir = path.join(top_dir, relative_dir) if not path.exists(target_dir): os.mkdir(target_dir) for dirname in dirs[:]: if dirname.startswith('.') or dirname == '__pycache__': dirs.remove(dirname) for filename in files: if filename.endswith(('.pyo', '.pyc', '.py.class')): # Ignore some files as they cause various breakages. continue old_path = path.join(root, filename) new_path = path.join(top_dir, relative_dir, filename.replace(base_name, name)) for old_suffix, new_suffix in self.rewrite_template_suffixes: if new_path.endswith(old_suffix): new_path = new_path[:-len(old_suffix)] + new_suffix break # Only rewrite once if path.exists(new_path): raise CommandError("%s already exists, overlaying a " "project or app into an existing " "directory won't replace conflicting " "files" % new_path) # Only render the Python files, as we don't want to # accidentally render Moose templates files if new_path.endswith(extensions) or filename in extra_files: with io.open(old_path, 'r', encoding='utf-8') as template_file: content = template_file.read() # TODO: use Jinja2 instead of implementing template engine self # template = Engine().from_string(content) # content = template.render(context) content = string.Template(content).substitute(**context) with io.open(new_path, 'w', encoding='utf-8') as new_file: new_file.write(content) else: shutil.copyfile(old_path, new_path) if self.verbosity >= 2: self.stdout.write("Creating %s\n" % new_path) try: shutil.copymode(old_path, new_path) self.make_writeable(new_path) except OSError: self.stderr.write( "Notice: Couldn't set permission bits on %s. You're " "probably using an uncommon filesystem setup. No " "problem." % new_path, self.style.NOTICE) if self.paths_to_remove: if self.verbosity >= 2: self.stdout.write("Cleaning up temporary files.\n") for path_to_remove in self.paths_to_remove: if path.isfile(path_to_remove): os.remove(path_to_remove) else: shutil.rmtree(path_to_remove)