Repository: getnikola/nikola

Issue:
Nikola new_post -F works weird
Because of the selective plugin disabling it now misses a few formats when called with a conf.py

Plan:
- [ ] Keep the data about disabled compilers in the Nikola class
- [ ] Refactor new_post so all the standalone functions that always get the same arguments go away



Discussion:

Resolving patch:
diff --git a/nikola/nikola.py b/nikola/nikola.py
--- 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
--- 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))


Test patch: