diff --git a/CHANGES.txt b/CHANGES.txt index 99b8b5ee10..e74b2ddb77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ New in Master Bugfixes -------- +* Refactor new_post to match lazy plugin loading (Issue #1943) * Make Nikola startup faster by not loading useless plugins (Issue #1825) * Ignore sliced multibyte characters when reading metadata for sitemaps * Fix NameError caused by failed import in auto plugin. diff --git a/nikola/nikola.py b/nikola/nikola.py index d9c96c94ee..caea7d1245 100644 --- a/nikola/nikola.py +++ b/nikola/nikola.py @@ -735,6 +735,7 @@ def __init__(self, **config): # And put them in "bad compilers" pp_exts = set([os.path.splitext(x[0])[1] for x in self.config['post_pages']]) self.config['COMPILERS'] = {} + self.disabled_compilers = {} bad_compilers = set([]) for k, v in compilers.items(): if pp_exts.intersection(v): @@ -749,13 +750,14 @@ def __init__(self, **config): # Remove compilers we don't use if p[-1].name in bad_compilers: bad_candidates.add(p) + self.disabled_compilers[p[-1].name] = p utils.LOGGER.debug('Not loading unneeded compiler {}', p[-1].name) # Remove blacklisted plugins if p[-1].name in self.config['DISABLED_PLUGINS']: bad_candidates.add(p) utils.LOGGER.debug('Not loading disabled plugin {}', p[-1].name) # Remove compiler extensions we don't need - if p[-1].details.has_option('Nikola', 'compiler') and p[-1].details.get('Nikola', 'compiler') in bad_compilers: + if p[-1].details.has_option('Nikola', 'compiler') and p[-1].details.get('Nikola', 'compiler') in self.disabled_compilers: bad_candidates.add(p) utils.LOGGER.debug('Not loading comopiler extension {}', p[-1].name) self.plugin_manager._candidates = list(set(self.plugin_manager._candidates) - bad_candidates) diff --git a/nikola/plugins/command/new_post.py b/nikola/plugins/command/new_post.py index 7c99827780..d7ebd5b0cb 100644 --- a/nikola/plugins/command/new_post.py +++ b/nikola/plugins/command/new_post.py @@ -46,103 +46,6 @@ LOGGER = POSTLOGGER -def filter_post_pages(compiler, is_post, compilers, post_pages, compiler_objs, compilers_raw): - """Return the correct entry from post_pages. - - Information based on: - * selected compilers - * available compilers - * post/page status - """ - # First throw away all the post_pages with the wrong is_post - filtered = [entry for entry in post_pages if entry[3] == is_post] - - # These are the extensions supported by the required format - extensions = compilers.get(compiler) - if extensions is None: - if compiler in compiler_objs: - LOGGER.error("There is a {0} compiler available, but it's not set in your COMPILERS option.".format(compiler)) - LOGGER.info("Read more: {0}".format(COMPILERS_DOC_LINK)) - else: - LOGGER.error('Unknown format {0}'.format(compiler)) - print_compilers(compilers_raw, post_pages, compiler_objs) - return False - - # Throw away the post_pages with the wrong extensions - filtered = [entry for entry in filtered if any([ext in entry[0] for ext in - extensions])] - - if not filtered: - type_name = "post" if is_post else "page" - LOGGER.error("Can't find a way, using your configuration, to create " - "a {0} in format {1}. You may want to tweak " - "COMPILERS or {2}S in conf.py".format( - type_name, compiler, type_name.upper())) - LOGGER.info("Read more: {0}".format(COMPILERS_DOC_LINK)) - - return False - return filtered[0] - - -def print_compilers(compilers_raw, post_pages, compiler_objs): - """List all available compilers in a human-friendly format. - - :param compilers_raw: The compilers dict, mapping compiler names to tuples of extensions - :param post_pages: The post_pages structure - :param compilers_objs: Compiler objects - """ - # We use compilers_raw, because the normal dict can contain - # garbage coming from the translation candidate implementation. - # Entries are in format: (name, extensions, used_in_post_pages) - parsed_compilers = {'used': [], 'unused': [], 'disabled': []} - - for compiler_name, compiler_obj in compiler_objs.items(): - fname = compiler_obj.friendly_name or compiler_name - if compiler_name not in compilers_raw: - parsed_compilers['disabled'].append((compiler_name, fname, (), False)) - else: - # stolen from filter_post_pages - extensions = compilers_raw[compiler_name] - filtered = [entry for entry in post_pages if any( - [ext in entry[0] for ext in extensions])] - if filtered: - parsed_compilers['used'].append((compiler_name, fname, extensions, True)) - else: - parsed_compilers['unused'].append((compiler_name, fname, extensions, False)) - - # Sort compilers alphabetically by name, just so it’s prettier (and - # deterministic) - parsed_compilers['used'].sort(key=operator.itemgetter(0)) - parsed_compilers['unused'].sort(key=operator.itemgetter(0)) - parsed_compilers['disabled'].sort(key=operator.itemgetter(0)) - - # We also group the compilers by status for readability. - parsed_list = parsed_compilers['used'] + parsed_compilers['unused'] + parsed_compilers['disabled'] - - print("Available input formats:\n") - - name_width = max([len(i[0]) for i in parsed_list] + [4]) # 4 == len('NAME') - fname_width = max([len(i[1]) for i in parsed_list] + [11]) # 11 == len('DESCRIPTION') - - print((' {0:<' + str(name_width) + '} {1:<' + str(fname_width) + '} EXTENSIONS\n').format('NAME', 'DESCRIPTION')) - - for name, fname, extensions, used in parsed_list: - flag = ' ' if used else '!' - flag = flag if extensions else '~' - - extensions = ', '.join(extensions) if extensions else '(disabled: not in COMPILERS)' - - print(('{flag}{name:<' + str(name_width) + '} {fname:<' + str(fname_width) + '} {extensions}').format(flag=flag, name=name, fname=fname, extensions=extensions)) - - print(""" -More compilers are available in the Plugins Index. - -Compilers marked with ! and ~ require additional configuration: - ! not in the PAGES/POSTS tuples (unused) - ~ not in the COMPILERS dict (disabled) -Read more: {0}""".format(COMPILERS_DOC_LINK)) - - def get_default_compiler(is_post, compilers, post_pages): """Given compilers and post_pages, return a reasonable default compiler for this kind of post/page.""" # First throw away all the post_pages with the wrong is_post @@ -333,7 +236,7 @@ def _execute(self, options, args): wants_available = options['available-formats'] if wants_available: - print_compilers(self.site.config['_COMPILERS_RAW'], self.site.config['post_pages'], self.site.compilers) + self.print_compilers() return if is_page: @@ -360,17 +263,13 @@ def _execute(self, options, args): if content_format not in compiler_names: LOGGER.error("Unknown {0} format {1}, maybe you need to install a plugin?".format(content_type, content_format)) - print_compilers(self.site.config['_COMPILERS_RAW'], self.site.config['post_pages'], self.site.compilers) + self.print_compilers() return compiler_plugin = self.site.plugin_manager.getPluginByName( content_format, "PageCompiler").plugin_object # Guess where we should put this - entry = filter_post_pages(content_format, is_post, - self.site.config['COMPILERS'], - self.site.config['post_pages'], - self.site.compilers, - self.site.config['_COMPILERS_RAW']) + entry = self.filter_post_pages(content_format, is_post) if entry is False: return 1 @@ -497,3 +396,118 @@ def _execute(self, options, args): subprocess.call(to_run) else: LOGGER.error('$EDITOR not set, cannot edit the post. Please do it manually.') + + def filter_post_pages(self, compiler, is_post): + """Return the correct entry from post_pages. + + Information based on: + * selected compilers + * available compilers + * post/page status + """ + compilers = self.site.config['COMPILERS'] + post_pages = self.site.config['post_pages'] + compiler_objs = self.site.compilers + + # First throw away all the post_pages with the wrong is_post + filtered = [entry for entry in post_pages if entry[3] == is_post] + + # These are the extensions supported by the required format + extensions = compilers.get(compiler) + if extensions is None: + if compiler in compiler_objs: + LOGGER.error("There is a {0} compiler available, but it's not set in your COMPILERS option.".format(compiler)) + LOGGER.info("Read more: {0}".format(COMPILERS_DOC_LINK)) + else: + LOGGER.error('Unknown format {0}'.format(compiler)) + self.print_compilers() + return False + + # Throw away the post_pages with the wrong extensions + filtered = [entry for entry in filtered if any([ext in entry[0] for ext in + extensions])] + + if not filtered: + type_name = "post" if is_post else "page" + LOGGER.error("Can't find a way, using your configuration, to create " + "a {0} in format {1}. You may want to tweak " + "COMPILERS or {2}S in conf.py".format( + type_name, compiler, type_name.upper())) + LOGGER.info("Read more: {0}".format(COMPILERS_DOC_LINK)) + + return False + return filtered[0] + + def print_compilers(self): + """List all available compilers in a human-friendly format.""" + # We use compilers_raw, because the normal dict can contain + # garbage coming from the translation candidate implementation. + # Entries are in format: (name, extensions, used_in_post_pages) + + compilers_raw = self.site.config['_COMPILERS_RAW'] + + used_compilers = [] + unused_compilers = [] + disabled_compilers = [] + + for name, plugin in self.site.compilers.items(): + if name in compilers_raw: + used_compilers.append([ + name, + plugin.friendly_name or name, + compilers_raw[name], + True + ]) + else: + disabled_compilers.append([ + name, + plugin.friendly_name or name, + (), + False + ]) + + for name in self.site.disabled_compilers: + if name in compilers_raw: + unused_compilers.append([ + name, + name, + compilers_raw[name], + False + ]) + else: + disabled_compilers.append([ + name, + name, + (), + False + ]) + + used_compilers.sort(key=operator.itemgetter(0)) + unused_compilers.sort(key=operator.itemgetter(0)) + disabled_compilers.sort(key=operator.itemgetter(0)) + + # We also group the compilers by status for readability. + parsed_list = used_compilers + unused_compilers + disabled_compilers + + print("Available input formats:\n") + + name_width = max([len(i[0]) for i in parsed_list] + [4]) # 4 == len('NAME') + fname_width = max([len(i[1]) for i in parsed_list] + [11]) # 11 == len('DESCRIPTION') + + print((' {0:<' + str(name_width) + '} {1:<' + str(fname_width) + '} EXTENSIONS\n').format('NAME', 'DESCRIPTION')) + + for name, fname, extensions, used in parsed_list: + flag = ' ' if used else '!' + flag = flag if extensions else '~' + + extensions = ', '.join(extensions) if extensions else '(disabled: not in COMPILERS)' + + print(('{flag}{name:<' + str(name_width) + '} {fname:<' + str(fname_width) + '} {extensions}').format(flag=flag, name=name, fname=fname, extensions=extensions)) + + print(""" + More compilers are available in the Plugins Index. + + Compilers marked with ! and ~ require additional configuration: + ! not in the PAGES/POSTS tuples (unused) + ~ not in the COMPILERS dict (disabled) + Read more: {0}""".format(COMPILERS_DOC_LINK))