0

I frequently have to show snippets of Python code in slides, and I like to show their behaviour in PythonTutor. Unfortunately, a mere \href{my_url}{my_title} is problematic, since the URL will contain a lot of characters like % and & which is a problem for LaTeX.

I can of course escape all of these instances, but is there a more elegant / practical solution? I'm aware of PythonTutor's shortened URL option which circumvents the problem but won't be ideal if I change my code; a preferred option would be a command that reads data from some myprogram.py file and generates a valid link to PythonTutor with the code.

Update

I found a fix while preparing an example as righfully suggested by moewe: just make the frame fragile. Compilation works, and link is fully functional; but I'd still be interested in a command like \linkmyfile{pathtofile}{mycaption} that would behave as described above.

\documentclass{beamer}

\begin{document}

\begin{frame}[fragile]
\centering
\href{http://www.pythontutor.com/visualize.html#code=print%28%22Hello%20World!%22%29&cumulative=true&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false}{\bf Visualize my code}
\end{frame}

\end{document}
  • 1
    See also this question and this question. – Karlo Jul 23 '18 at 14:03
  • 3
    Usually URLs with special characters are handled gracefully by url and hyperref. The macros of these packages are a bit fragile, however, so there are certain situations in which they don't work as intended (Karlo's link is about footnotes for example). Whether or not your use case is such a situation, we can only tell if you show us an example of what you intend to do and what does not work. – moewe Jul 23 '18 at 14:43
  • @moewe: fragile indeed ... I edited my question accordingly, thanks. – Anthony Labarre Jul 23 '18 at 16:41
  • 1
    For your linkmyfile command, you could write a small python script to post your file online, and return the formatted url. You could then call this script with pythontex – Ash Jul 23 '18 at 16:59
  • BTW: The two-letter font commands \bf and friends have ben deprecated for the better part of 25 years now: tex.stackexchange.com/q/15361/35864 – moewe Jul 24 '18 at 5:52
2

A command \pythontutorlink{filename}{label} to automatically link to that website is not too hard to implement. The crucial parts are

  1. Reading the Python file and storing the result,
  2. escaping all non-letter and non-digit characters into the corresponding %-sequence,
  3. and finally creating the link using \href.

Reading the file without having LaTeX complain about special characters, unbalanced braces etc. seemed tricky at first, but it's pretty simple using the catchfile package. That package already provides a command \CatchFileDef which reads a file and stores its contents in a new macro. Before we read the file, we need to make sure that special characters are handled correctly. This can be done by changing the catcodes of all special characters to class "other" (done in \@setupcatcodes).

We now have the file contents stored in \@tempfile. Next is building a URL from it. The final URL will be stored in a token register \@encodedurl which starts prefilled with the part of the URL before the actual code part. Now we map each character from the file contents to its corresponding %-sequence or keep it the same if it's a letter or digit. \@urlencode does this whole replacement by traversing its parameter text and calling \@encodechar on each character, which appends the appropriate result to \@encodedurl. Finally, some additional URL query parameters can be added, and then the result is passed to \href to produce the final output.

Here's the full code with an example link:

\documentclass{article}

\usepackage{filecontents}
\usepackage{catchfile}
\usepackage{hyperref}

\begin{filecontents*}{fibs.py}
def F(n):
    if n == 0: return 0
    elif n == 1: return 1
    else: return F(n-1)+F(n-2)
print(F(6))
\end{filecontents*}


\makeatletter
\endlinechar=-1

\newtoks\@encodedurl

\def\@addtourl#1{\global\addto@hook\@encodedurl{#1}}
\def\@addtourlesc#1{\expandafter\@addtourl\expandafter{\@perc#1}}

{
    \catcode`\#=12\relax
    \catcode`\%=12\relax
    \catcode`\&=12\relax
    \gdef\@perc{%}
    \gdef\@hash{#}
    \gdef\@amp{&}
}

\def\@urlencode#1{\@@urlencode#1{}\@end}
\def\@@urlencode#1#2\@end{
    \@encodechar{#1}
    \if\relax\detokenize{#2}\relax
    \else
        \@@urlencode#2\@end
    \fi
}
\def\@encodechar#1{
    \ifnum`#1=`\^^M \@addtourlesc{0D}\else
    \ifnum`#1=`\  \@addtourl{+}\else
    \ifnum`#1=`\! \@addtourlesc{21}\else
    \ifnum`#1=`\" \@addtourlesc{22}\else
    \ifnum`#1=`\# \@addtourlesc{23}\else
    \ifnum`#1=`\$ \@addtourlesc{24}\else
    \ifnum`#1=`\% \@addtourlesc{25}\else
    \ifnum`#1=`\& \@addtourlesc{26}\else
    \ifnum`#1=`\' \@addtourlesc{27}\else
    \ifnum`#1=`\( \@addtourlesc{28}\else
    \ifnum`#1=`\) \@addtourlesc{29}\else
    \ifnum`#1=`\* \@addtourlesc{2A}\else
    \ifnum`#1=`\+ \@addtourlesc{2B}\else
    \ifnum`#1=`\, \@addtourlesc{2C}\else
    \ifnum`#1=`\- \@addtourlesc{2D}\else
    \ifnum`#1=`\. \@addtourlesc{2E}\else
    \ifnum`#1=`\/ \@addtourlesc{2F}\else
    \ifnum`#1=`\: \@addtourlesc{3A}\else
    \ifnum`#1=`\; \@addtourlesc{3B}\else
    \ifnum`#1=`\< \@addtourlesc{3C}\else
    \ifnum`#1=`\= \@addtourlesc{3D}\else
    \ifnum`#1=`\> \@addtourlesc{3E}\else
    \ifnum`#1=`\? \@addtourlesc{3F}\else
    \ifnum`#1=`\@ \@addtourlesc{40}\else
    \ifnum`#1=`\[ \@addtourlesc{5B}\else
    \ifnum`#1=`\\ \@addtourlesc{5C}\else
    \ifnum`#1=`\] \@addtourlesc{5D}\else
    \ifnum`#1=`\{ \@addtourlesc{7B}\else
    \ifnum`#1=`\| \@addtourlesc{7C}\else
    \ifnum`#1=`\} \@addtourlesc{7D}\else
    \@addtourl{#1}
    \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
}

\def\@setupcatcodes{
    \let\do=\@makeother\dospecials
    \@makeother\ \relax
    \@makeother\^^M\relax
}

\def\pythontutorlink#1#2{
    \begingroup
    \@setupcatcodes
    \CatchFileDef\@tempfile{#1}{}
    \global\@encodedurl={http://www.pythontutor.com/visualize.html\@hash code=}
    \expandafter\@urlencode\expandafter{\@tempfile}
    \@addtourl{\@amp mode=display}
    \expandafter\href\expandafter{\the\@encodedurl}{#2}
    \endgroup
}

\endlinechar=`\^^M
\makeatother

\begin{document}

Example link: \pythontutorlink{fibs.py}{Fibonacci numbers}

\end{document}

If other non-ASCII characters occur in the Python code, more character mappings may have to be added. An interesting addition would also be to use macros similar to the ones in A fully expandable conversion to hexadecimal numbers to directly calculate the hexcode from the character code, especially when used with XeLaTeX and Unicode input.

  • Thanks, I'm glad you found a solution that avoids pythontex, which made the compilation more complicated. – Anthony Labarre Jul 31 '18 at 9:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.