Repository: getnikola/nikola

Issue:
Cannot build site with Pygments 2.12.0
<!--
Before creating an issue:
* make sure you are using an up-to-date version of Nikola
* search for existing issues that might be related

Make sure to:
* provide information about your environment (below)
* include all the output you get, and any other information related to your problem

Nikola v7.6.4, as provided by Ubuntu, is NOT SUPPORTED.
If you are using this version, you should upgrade: https://getnikola.com/getting-started.html
-->

### Environment

**Python Version:**
3.10
**Nikola Version:**
8.2.1
**Operating System:**
Ubuntu (nikola action)
### Description:

Nikola fails to render post with pygments code blocks on latest release 8.2.1, which used to render perfectly fine before.

```bash
  PythonAction Error
  Traceback (most recent call last):
    File "/usr/local/lib/python3.10/site-packages/pygments/__init__.py", line 61, in format
      formatter.format(tokens, realoutfile)
    File "/usr/local/lib/python3.10/site-packages/pygments/formatter.py", line 94, in format
      return self.format_unencoded(tokensource, outfile)
    File "/usr/local/lib/python3.10/site-packages/nikola/packages/pygments_better_html/__init__.py", line 222, in format_unencoded
      return super().format_unencoded(tokensource, outfile)
    File "/usr/local/lib/python3.10/site-packages/pygments/formatters/html.py", line 981, in format_unencoded
      source = self.wrap(source)
  TypeError: NikolaPygmentsHTML.wrap() missing 1 required positional argument: 'outfile'
  
  During handling of the above exception, another exception occurred:
  
  Traceback (most recent call last):
    File "/usr/local/lib/python3.10/site-packages/doit/action.py", line 461, in execute
      returned_value = self.py_callable(*self.args, **kwargs)
    File "/usr/local/lib/python3.10/site-packages/nikola/post.py", line 729, in compile
      self.compile_html(
    File "/usr/local/lib/python3.10/site-packages/nikola/plugins/compile/rest/__init__.py", line 151, in compile
      output, error_level, deps, shortcode_deps = self.compile_string(data, source, is_two_file, post, lang)
    File "/usr/local/lib/python3.10/site-packages/nikola/plugins/compile/rest/__init__.py", line 134, in compile_string
      output, error_level, deps, _ = rst2html(
    File "/usr/local/lib/python3.10/site-packages/nikola/plugins/compile/rest/__init__.py", line 382, in rst2html
      pub.publish(enable_exit_status=enable_exit_status)
    File "/usr/local/lib/python3.10/site-packages/docutils/core.py", line 217, in publish
      self.document = self.reader.read(self.source, self.parser,
    File "/usr/local/lib/python3.10/site-packages/docutils/readers/__init__.py", line 72, in read
      self.parse()
    File "/usr/local/lib/python3.10/site-packages/docutils/readers/__init__.py", line 78, in parse
      self.parser.parse(self.input, document)
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/__init__.py", line 183, in parse
      self.statemachine.run(inputlines, document, inliner=self.inliner)
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/states.py", line 170, in run
      results = StateMachineWS.run(self, input_lines, input_offset,
    File "/usr/local/lib/python3.10/site-packages/docutils/statemachine.py", line 240, in run
      context, next_state, result = self.check_line(
    File "/usr/local/lib/python3.10/site-packages/docutils/statemachine.py", line 452, in check_line
      return method(match, context, next_state)
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/states.py", line 2352, in explicit_markup
      nodelist, blank_finish = self.explicit_construct(match)
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/states.py", line 2364, in explicit_construct
      return method(self, expmatch)
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/states.py", line 2101, in directive
      return self.run_directive(
    File "/usr/local/lib/python3.10/site-packages/docutils/parsers/rst/states.py", line 2151, in run_directive
      result = directive_instance.run()
    File "/usr/local/lib/python3.10/site-packages/nikola/plugins/compile/rest/listing.py", line 134, in run
      out = pygments.highlight(code, lexer, formatter)
    File "/usr/local/lib/python3.10/site-packages/pygments/__init__.py", line 82, in highlight
      return format(lex(code, lexer), formatter, outfile)
    File "/usr/local/lib/python3.10/site-packages/pygments/__init__.py", line 69, in format
      raise TypeError('format() argument must be a formatter instance, '
  TypeError: format() argument must be a formatter instance, not a class
``` 

if this helps, this is how the code snippets look like in this one:

```
.. code-block:: python

   int("0111", 2)
   >>> 7
```
Incompatibility with newer pygments version or similar(?)
### Environment

**Python Version:** 3.10

**Nikola Version:** Nikola v8.2.1

**Operating System:** Linux

### Description:

I'm not totally sure what caused this, except I upgraded everything in my virtualenv and it started happening.

Using a code block in a post (presumably) causes this exception to happen:
```
TaskError - taskid:render_posts:cache/posts/post-name-here.html
PythonAction Error
Traceback (most recent call last):
  File ".../venv/lib/python3.10/site-packages/pygments/__init__.py", line 61, in format
    formatter.format(tokens, realoutfile)
  File ".../venv/lib/python3.10/site-packages/pygments/formatter.py", line 94, in format
    return self.format_unencoded(tokensource, outfile)
  File ".../venv/lib/python3.10/site-packages/nikola/packages/pygments_better_html/__init__.py", line 222, in format_unencoded
    return super().format_unencoded(tokensource, outfile)
  File ".../venv/lib/python3.10/site-packages/pygments/formatters/html.py", line 981, in format_unencoded
    source = self.wrap(source)
TypeError: NikolaPygmentsHTML.wrap() missing 1 required positional argument: 'outfile'

During handling of the above exception, another exception occurred:

[...]
```

The following change gets rid of the exception and output looks fine:
```diff
diff --git a/nikola/utils.py b/nikola/utils.py
index e2a64a3cc..4323aa908 100644
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -1690,7 +1690,7 @@ class NikolaPygmentsHTML(BetterHtmlFormatter):
         kwargs['nowrap'] = False
         super().__init__(**kwargs)
 
-    def wrap(self, source, outfile):
+    def wrap(self, source, *_):
         """Wrap the ``source``, which is a generator yielding individual lines, in custom generators."""
         style = []
         if self.prestyles:
``` 


Discussion:

Duplicate of #3617 (with a time difference of two minutes, so it’s not your fault or anything :))
Ahhh what a coincidence :smile: 

Resolving patch:
diff --git a/nikola/plugins/compile/markdown/__init__.py b/nikola/plugins/compile/markdown/__init__.py
--- a/nikola/plugins/compile/markdown/__init__.py
+++ b/nikola/plugins/compile/markdown/__init__.py
@@ -33,13 +33,20 @@
 
 from nikola import shortcodes as sc
 from nikola.plugin_categories import PageCompiler
-from nikola.utils import makedirs, req_missing, write_metadata, LocaleBorg, map_metadata
+from nikola.utils import makedirs, req_missing, write_metadata, LocaleBorg, map_metadata, NikolaPygmentsHTML
 
 try:
     from markdown import Markdown
 except ImportError:
     Markdown = None
 
+# Override Pygments formatter for Markdown.
+try:
+    import markdown.extensions.codehilite
+    markdown.extensions.codehilite.get_formatter_by_name = lambda _, **args: NikolaPygmentsHTML(**args)
+except ImportError:
+    pass
+
 
 class ThreadLocalMarkdown(threading.local):
     """Convert Markdown to HTML using per-thread Markdown objects.
diff --git a/nikola/utils.py b/nikola/utils.py
--- a/nikola/utils.py
+++ b/nikola/utils.py
@@ -1690,7 +1690,7 @@ def __init__(self, anchor_ref=None, classes=None, **kwargs):
         kwargs['nowrap'] = False
         super().__init__(**kwargs)
 
-    def wrap(self, source, outfile):
+    def wrap(self, source, *args):
         """Wrap the ``source``, which is a generator yielding individual lines, in custom generators."""
         style = []
         if self.prestyles:
@@ -1708,6 +1708,18 @@ def wrap(self, source, outfile):
 
 # For consistency, override the default formatter.
 pygments.formatters._formatter_cache['HTML'] = NikolaPygmentsHTML
+pygments.formatters._formatter_cache['html'] = NikolaPygmentsHTML
+_original_find_formatter_class = pygments.formatters.find_formatter_class
+
+
+def nikola_find_formatter_class(alias):
+    """A Nikola-specific version of find_formatter_class."""
+    if alias.lower().contains('html'):
+        return NikolaPygmentsHTML
+    return _original_find_formatter_class(alias)
+
+
+pygments.formatters.find_formatter_class = nikola_find_formatter_class
 
 
 def get_displayed_page_number(i, num_pages, site):


Test patch:
diff --git a/tests/test_compile_markdown.py b/tests/test_compile_markdown.py
--- a/tests/test_compile_markdown.py
+++ b/tests/test_compile_markdown.py
@@ -28,12 +28,10 @@
     from this
 """,
             """\
-<table class="codehilitetable"><tr><td class="linenos">\
-<div class="linenodiv"><pre><span class="normal">1</span></pre></div>\
-</td><td class="code"><pre class="code literal-block"><span></span>\
-<code><span class="kn">from</span> <span class="nn">this</span>
-</code></pre>
-</td></tr></table>
+<div class="code"><table class="codetable"><tr><td class="linenos linenodiv">\
+<a href="#-1"><code data-line-number="1"></code></a></td>\
+<td class="code"><code><span class="kn">from</span> <span class="nn">this</span>
+</code></td></tr></table></div>
 """,
             id="hilite",
         ),