gpoore / minted

minted is a LaTeX package that provides syntax highlighting using the Pygments library. Highlighted source code can be customized using fancyvrb.
1.73k stars 125 forks source link

Confused by arbitrary failure #368

Closed mattharrison closed 12 months ago

mattharrison commented 1 year ago

I'm making a book using minted but I spend (too much) time debugging minted issues.

For example, this sample fails to build for me. I get the following error:

% rubber --module xelatex --shell-escape bug.tex
Rubber invoked from VCS, prepending /Users/matt/Dropbox/work/pylib/rubber3 to sys.path
compiling bug.tex
executing: xelatex --shell-escape \nonstopmode \input{bug.tex}
Running xelatex resulted in a non-zero exit status.
There were errors compiling bug.tex: Recipe for bug.pdf failed.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Something's wrong--perhaps a missing \item.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Something's wrong--perhaps a missing \item.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Missing } inserted.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Missing } inserted.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Missing } inserted.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Extra }, or forgotten \endgroup.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Extra }, or forgotten \endgroup.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: Extra }, or forgotten \endgroup.
/tmp/91A8E376AC74D855FA3944C0B7FC6AD4AA0F2A48A3BB41E9972AE82845B2144D.pygtex:7: leading text: \end{Verbatim}
./bug.tex:369: File ended while scanning use of \FancyVerbGetLine.
./bug.tex:369: leading text: \end{minted}
./bug.tex:369: FancyVerb Error:
./bug.tex:369: leading text: \end{minted}
More errors.

However, changing the code at line 369 doesn't help. It appears the error is before that.

I've pasted an example file below.

I'm running % rubber --module xelatex --shell-escape bug.tex to build the test file.

However, if you search for "My confusion" and comment out the code directly below it, the file builds (this code is not line 369).

% run command:
% rubber --module xelatex --shell-escape bug.tex

  \documentclass[11pt]{memoir}

  \setstocksize{9in}{6in}
  \settrimmedsize{\stockheight}{\stockwidth}{*}
  \setulmarginsandblock{0.75in}{0.75in}{*}
  \setlrmarginsandblock{0.8in}{0.55in}{*}
  \setheaderspaces{0.5in}{*}{*}
  % Need this after tweaking above
  \checkandfixthelayout

   \usepackage[cache=true,cachedir=/tmp,newfloat=true]{minted}
  % better frames - used for Note section
  \usepackage{mdframed}
  \usepackage[breaklinks,linktocpage=true]{hyperref}
  \usepackage{needspace}

  \usepackage{color}
  \definecolor{litegreen}{rgb}{0.95,0.95,0.85}
  \definecolor{mygreen}{rgb}{0,0.6,0}

  \usepackage{fontspec}
  \usepackage[Latin,Greek,Emoticons]{ucharclasses}
  \newfontfamily\mydef{DejaVu Sans}
  \newfontfamily\mynormal{Palatino}
  \setTransitionsFor{Emoticons}{\begingroup\mydef}{\endgroup}

  \newfontfamily\headfont{Roboto Condensed}
  %\setchapheadstyle{\headfont}% Set \section style
  \setsecheadstyle{\headfont}% Set \section style
  \renewcommand{\chaptitlefont}{\headfont\huge}
  \renewcommand{\chapnumfont}{\headfont\huge}
  \renewcommand{\chapnamefont}{\headfont\huge}
  \renewcommand{\partnumfont}{\headfont}
  \renewcommand{\partnamefont}{\headfont}
  \renewcommand{\parttitlefont}{\headfont}

  \usepackage{longtable}

\begin{document}

  % - definitions
  \newcommand{\Author}{Matt Harrison}
  \newcommand{\Title}{A book}

  \frontmatter

  % half title page
  \pagestyle{empty}
  \begin{center}
  \huge{\Title}
  \vskip25pt
  \end{center}
  \clearpage

  % also available - on verso
  \newpage
  \thispagestyle{empty}
  \mbox{}
  \clearpage

  % full title
  \pagestyle{empty}
  \begin{center}
  \huge{\Title}
  \vskip25pt
  \vskip25pt
  \large{}
  \Author
  \vskip25pt
  \end{center}
  \null\vfill
  \begin{center}
  \end{center}
  \clearpage

  % copyright
  \null\vfill
  \begin{flushleft}
  COPYRIGHT © 2023
  \bigskip
  \vskip5pt
  While every precaution has been taken in the preparation of this book, the publisher and author assumes no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.
  \end{flushleft}
  \clearpage
  \pagestyle{ruled}
  \aliaspagestyle{chapter}{ruled}
  \tableofcontents*
  % \dedicationpage
  \mainmatter

\hypertarget{conditionals-and-whitespace}{%
\chapter{Conditionals and
Whitespace}\label{conditionals-and-whitespace}}

In this chapter, you will learn more about making comparisons in Python.
Most code needs to decide which path to execute, so you will look at how
this is done.

In addition to the boolean values, \mintinline[]{text}{True} and
\mintinline[]{text}{False}, in Python, you can also use expressions to
get boolean values. If you have two numbers, you might want to compare
them to check if they are greater than or less than each other. The
operators, \mintinline[]{text}{>} and \mintinline[]{text}{<}, do this
respectively:

\begin{minted}[]{pycon}
>>> 5 > 9
False
\end{minted}

These operations work on most types. If you create a custom class that
defines the appropriate magic methods, your class can use them as well:

My confusion. If I comment out below it works!

\begin{minted}[]{pycon}
>>> name = 'Matt'
>>> name == 'Matt'
True
>>> name != 'Fred'
True
>>> 1 > 3
False
\end{minted}

\index{\mintinline[]{text}{functools.total_ordering}}

\begin{mdframed}[needspace=6.5em,frametitle={\headfont  Note

},backgroundcolor=litegreen,linewidth=0pt]

The ``rich comparison'' magic methods, \mintinline[]{text}{__gt__},
\mintinline[]{text}{__lt__}, \mintinline[]{text}{__ge__},
\mintinline[]{text}{__le__}, \mintinline[]{text}{__eq__}, and
\mintinline[]{text}{__ne__} correspond to \mintinline[]{text}{>},
\mintinline[]{text}{<}, \mintinline[]{text}{>=},
\mintinline[]{text}{<=}, \mintinline[]{text}{==}, and
\mintinline[]{text}{!=} respectively. Defining all of these can be
somewhat tedious and repetitive. For classes where these comparisons are
commonly used, the \mintinline[]{text}{functools.total_ordering} class
decorator gives you all of the comparison functionality as long as you
define \mintinline[]{text}{__eq__} and \mintinline[]{text}{__le__}. The
decorator will automatically derive the remainder of the comparison
methods. Otherwise, all six methods should be implemented:

\begin{minted}[]{pycon}
>>> import functools
>>> @functools.total_ordering
... class Abs(object):
...     def __init__(self, num):
...         self.num = abs(num)
...     def __eq__(self, other):
...         return self.num == abs(other)
...     def __lt__(self, other):
...         return self.num < abs(other)
>>> five = Abs(-5)
>>> four = Abs(-4)
>>> five > four  # not using less than!
True
\end{minted}

Decorators are considered an intermediate subject and are not covered in
this beginning book.

\end{mdframed}

\begin{mdframed}[needspace=6.5em,frametitle={\headfont  Note

},backgroundcolor=litegreen,linewidth=0pt]

The \mintinline[]{text}{is} and \mintinline[]{text}{is not} statements
are for comparing \emph{identity}. When testing for identity---if two
objects are the same actual object with the same \mintinline[]{text}{id}
(not just the same value)---use \mintinline[]{text}{is} or
\mintinline[]{text}{is not}. Since \mintinline[]{text}{None} is a
singleton and only has one identity, \mintinline[]{text}{is} and
\mintinline[]{text}{is not} are used with \mintinline[]{text}{None}:

\begin{minted}[]{pycon}
>>> if name is None:
...     # initialize name
\end{minted}

\end{mdframed}

\hypertarget{combining-conditionals}{%
\section{Combining conditionals}\label{combining-conditionals}}

\index{\mintinline[]{text}{and}}

\index{\mintinline[]{text}{or}}

\index{\mintinline[]{text}{not}}

Conditional expressions are combined using \emph{boolean logic}. This
logic consists of the \mintinline[]{text}{and}, \mintinline[]{text}{or},
and \mintinline[]{text}{not} operators.

\begin{longtable}[]{@{}
  >{\raggedright\arraybackslash}p{(\columnwidth - 2\tabcolsep) * \real{0.2169}}
  >{\raggedright\arraybackslash}p{(\columnwidth - 2\tabcolsep) * \real{0.7831}}@{}}
\toprule\noalign{}
\begin{minipage}[b]{\linewidth}\raggedright
Boolean Operator
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
Meaning
\end{minipage} \\
\midrule\noalign{}
\endhead
\bottomrule\noalign{}
\endlastfoot
\mintinline[]{text}{x and y} & Both \mintinline[]{text}{x} and
\mintinline[]{text}{y} must evaluate to \mintinline[]{text}{True} for
true result \\
\mintinline[]{text}{x or y} & If \mintinline[]{text}{x} or
\mintinline[]{text}{y} is \mintinline[]{text}{True}, result is true \\
\mintinline[]{text}{not x} & Negate the value of \mintinline[]{text}{x}
(\mintinline[]{text}{True} becomes \mintinline[]{text}{False} and vice
versa) \\
\end{longtable}

Below is a simple example of setting a grade based on a
\mintinline[]{text}{score} using \mintinline[]{text}{and} to test
whether the score is between two numbers:

\begin{minted}[]{pycon}
>>> score = 91
>>> if score > 90 and score <= 100:
...     grade = 'A'
\end{minted}

\index{range comparison}

\begin{mdframed}[needspace=6.5em,frametitle={\headfont  Note

},backgroundcolor=litegreen,linewidth=0pt]

Python allows you to do the above example using a \emph{range
comparison} like this:

\begin{minted}[]{pycon}
>>> if 90 < score <=100:
...     grade = 'A'
\end{minted}

Either style works, but range comparisons are not common in other
languages.

\end{mdframed}

Here is an example for checking if a given name is a member of a band:

\begin{minted}[]{pycon}
>>> name = 'Paul'
>>> beatle = False
>>> if name == 'George' or \
...     name == 'Ringo' or \
...     name == 'John' or \
...     name == 'Paul':
...     beatle = True
... else:
...     beatle = False
\end{minted}

\begin{mdframed}[needspace=6.5em,frametitle={\headfont  Note

},backgroundcolor=litegreen,linewidth=0pt]

In the above example, the \mintinline[]{text}{\} at the end of
``\mintinline[]{text}{'George' or \}'' indicates that the statement will
be continued on the next line.

Like most programming languages, Python allows you to wrap conditional
statements in parentheses. Because they are not required in Python, most
developers leave them out unless they are needed for operator
precedence. But another subtlety of using parentheses is that they serve
as a hint to the interpreter when a statement is still open and will be
continued on the next line. Hence the \mintinline[]{text}{\} is not
needed in that case:

\begin{minted}[]{pycon}
>>> name = 'Paul'
>>> beatle = False
>>> if (name == 'George' or
...     name == 'Ringo' or
...     name == 'John' or
...     name == 'Paul'):
...     beatle = True
... else:
...     beatle = False
\end{minted}

\end{mdframed}

\begin{mdframed}[needspace=6.5em,frametitle={\headfont  Note

},backgroundcolor=litegreen,linewidth=0pt]

An idiomatic Python version of checking membership is listed below. To
check if a value is found across a variety of values, you can throw the
values in a set and use the \mintinline[]{text}{in} operator:

\begin{minted}[]{pycon}
>>> beatles = {'George', 'Ringo', 'John', 'Paul'}
>>> beatle = name in beatles
\end{minted}

A later chapter will discuss sets further.

\end{mdframed}

Here is an example of using the \mintinline[]{text}{not} keyword in a
conditional statement:

\begin{minted}[]{pycon}
>>> last_name = 'unknown'
>>> if name == 'Paul' and not beatle:
...     last_name = 'Revere'
\end{minted}

\hypertarget{if-statements}{%
\section{\texorpdfstring{\mintinline[]{text}{if}
statements}{ statements}}\label{if-statements}}

\index{\mintinline[]{text}{if}}

\index{\mintinline[]{text}{elif}}

\index{\mintinline[]{text}{else}}

Booleans (\mintinline[]{text}{True} and \mintinline[]{text}{False}) are
often used in \emph{conditional} statements. Conditional statements are
instructions that say, ``if this statement is true, perform a block of
code, otherwise execute some other code.'' Branching statements are used
frequently in Python. Sometimes, the ``if statement'' will check values
that contain booleans, other times it will check \emph{expressions} that
evaluate to booleans. Another common check is for implicit coercion to
``truthy'' or ``falsey'' values:

\begin{minted}[]{pycon}
>>> debug = True
>>> if debug:  # checking a boolean
...     print("Debugging")
Debugging
\end{minted}

\hypertarget{else-statements}{%
\section{\texorpdfstring{\mintinline[]{text}{else}
statements}{ statements}}\label{else-statements}}

An \mintinline[]{text}{else} statement can be used in combination with
an \mintinline[]{text}{if} statement. The body of the
\mintinline[]{text}{else} statement will execute only if the
\mintinline[]{text}{if} statement evaluates to
\mintinline[]{text}{False}. Here is an example of combining an
\mintinline[]{text}{else} statement with an \mintinline[]{text}{if}
statement. The school below appears to have grade inflation:

\begin{minted}[]{pycon}
>>> score = 87
>>> if score >= 90:
...     grade = 'A'
... else:
...     grade = 'B'
\end{minted}

\backmatter

\printindex

% add some empty pages at end
\clearpage
\clearpage
\newpage
\thispagestyle{empty}
\mbox{}
\newpage
\thispagestyle{empty}
\mbox{}

\end{document}
gpoore commented 1 year ago

I suspect that this is due to using \mintinline inside \section with

\hypertarget{if-statements}{%
\section{\texorpdfstring{\mintinline[]{text}{if}
statements}{ statements}}\label{if-statements}}

and

\hypertarget{else-statements}{%
\section{\texorpdfstring{\mintinline[]{text}{else}
statements}{ statements}}\label{else-statements}}

Swapping \mintinline for \Verb{if} and \Verb{else} works for me. \Verb is defined by fvextra, and \mintinline uses fvextra internally so I would have expected both to behave identically, but apparently something is different in this case.

mattharrison commented 1 year ago

Thanks for the response. Any indication why commenting out non-section code allows it to work in sections?

gpoore commented 1 year ago

My guess is that the section code (which is interacting with the table of contents) fails to open or more likely fails to close a sequence of commands (basically equivalent to an environment that isn't properly closed), and then the unopened/unclosed state is fixed if the right sequence of non-section code appears later in the document. However, given the state of LaTeX error messages, it can be nearly impossible to track down the details of what is happening in some of these cases. I think I found multiple parts of the document that when removed allowed it to compile successfully.

The last minted release (2.7) reimplemented \mintinline so that it would work better inside other commands, like \section. However, since \Verb works here and \mintinline doesn't, there may be some remaining issues. Unless there is a totally separate, unrelated bug or issue that we're both missing that is causing this. At least from the minted side, the \mintinline in sections is the only thing that seems potentially problematic here, and switching to \Verb does work.

mattharrison commented 1 year ago

I'm using pandoc and pandoc-minted. So I removed any mintinline creations from the pandoc-minted plugin.

After jumping through another hoop ( https://tex.stackexchange.com/questions/176113/problem-with-in-pdf-bookmark-under-xelatex ) I appear to be creating PDFs again. Thanks for your help.

gpoore commented 1 year ago

I've done some additional experiments, and it appears that memoir redefines \section so that it is incompatible with \mintinline (but only in some situations). Please leave this issue open for now.

mattharrison commented 1 year ago

For posterity's sake, it appears that index entries also have issues with minted (though it might be that the Pandoc plugin is naive).

On Sat, Jun 17, 2023, 7:44 AM Geoffrey Poore @.***> wrote:

I've done some additional experiments, and it appears that memoir redefines \section so that it is incompatible with \mintinline (but only in some situations). Please leave this issue open for now.

  • The documentation for \mintinline needs a note that it may not work inside \section etc. when using some document classes.
  • It may be worth creating an option that causes \mintinline to behave like \Verb when used inside other commands like \section.

— Reply to this email directly, view it on GitHub https://github.com/gpoore/minted/issues/368#issuecomment-1595764210, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA5E3LVNNFT3TGIJVJ7RUDXLWYE5ANCNFSM6AAAAAAZJQCJ5Q . You are receiving this because you authored the thread.Message ID: @.***>