Skip to content

Commit 46b3664

Browse files
committed
Enable "data-cite" HTML tags in Markdown cells
Fixes #22.
1 parent 92e9547 commit 46b3664

File tree

5 files changed

+159
-6
lines changed

5 files changed

+159
-6
lines changed

doc/a-normal-rst-file.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,55 @@ Domain Objects
163163
:ref:`this section </markdown-cells.ipynb#Links-to-Domain-Objects>`.
164164

165165
:param str foo: Example string parameter
166+
167+
Citations
168+
---------
169+
170+
You could use `standard Sphinx citations`_, but it might be more practical to
171+
use the sphinxcontrib.bibtex_ extension.
172+
173+
If you install and enable this extension,
174+
you can create citations like :cite:`perez2011python`:
175+
176+
.. code-block:: rst
177+
178+
:cite:`perez2011python`
179+
180+
You can create similar citations in Jupyter notebooks with a special HTML
181+
syntax, see the section about `citations in Markdown cells`_.
182+
183+
.. _standard Sphinx Citations: https://www.sphinx-doc.org/en/master/usage/
184+
restructuredtext/basics.html#citations
185+
.. _sphinxcontrib.bibtex: https://sphinxcontrib-bibtex.readthedocs.io/
186+
.. _citations in Markdown cells: markdown-cells.ipynb#Citations
187+
188+
For those citations to work, you also need to specify a BibTeX file,
189+
as explained in the next section.
190+
191+
192+
References
193+
----------
194+
195+
After installing and enabling the sphinxcontrib.bibtex_ extension,
196+
you can create a list of references from a BibTeX file like this:
197+
198+
.. code-block:: rst
199+
200+
.. bibliography:: references.bib
201+
202+
Have a look at the documentation for all the available options.
203+
204+
The list of references may look something like this:
205+
206+
.. bibliography:: references.bib
207+
:all:
208+
:style: alpha
209+
210+
However, in the LaTeX/PDF output the list of references will not appear here,
211+
but at the end of the document.
212+
For a possible work-around,
213+
see https://github.com/mcmtroffaes/sphinxcontrib-bibtex/issues/156.
214+
215+
There is an alternative Sphinx extension for creating bibliographies:
216+
https://bitbucket.org/wnielson/sphinx-natbib/.
217+
However, this project seems to be abandoned (last commit in 2011).

doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
'TeX': {'equationNumbers': {'autoNumber': 'AMS', 'useLabelIds': True}},
101101
}
102102

103-
# TODO: explain!
103+
# Additional files needed for generating LaTeX/PDF output:
104104
latex_additional_files = ['references.bib']
105105

106106
# -- The settings below this line are not specific to nbsphinx ------------

doc/markdown-cells.ipynb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,36 @@
153153
"The above equation has the number \\ref{pythagoras}."
154154
]
155155
},
156+
{
157+
"cell_type": "markdown",
158+
"metadata": {},
159+
"source": [
160+
"## Citations\n",
161+
"\n",
162+
"According to https://nbconvert.readthedocs.io/en/latest/latex_citations.html,\n",
163+
"`nbconvert` supports citations using a special HTML-based syntax.\n",
164+
"`nbsphinx` supports the same syntax.\n",
165+
"\n",
166+
"Example: <cite data-cite=\"kluyver2016jupyter\">Kluyver et al. (2016)</cite>.\n",
167+
"\n",
168+
"```html\n",
169+
"<cite data-cite=\"kluyver2016jupyter\">Kluyver et al. (2016)</cite>\n",
170+
"```\n",
171+
"\n",
172+
"You don't actually have to use `<cite>`,\n",
173+
"any inline HTML tag can be used, e.g. `<strong>`:\n",
174+
"<strong data-cite=\"perez2011python\">Python: An Ecosystem for Scientific Computing</strong>.\n",
175+
"\n",
176+
"```html\n",
177+
"<strong data-cite=\"perez2011python\">Python: An Ecosystem for Scientific Computing</strong>\n",
178+
"```\n",
179+
"\n",
180+
"You'll also have to define a list of references,\n",
181+
"see [the section about references](a-normal-rst-file.rst#references).\n",
182+
"\n",
183+
"There is also a Notebook extension which may or may not be useful: https://github.com/takluyver/cite2c."
184+
]
185+
},
156186
{
157187
"cell_type": "markdown",
158188
"metadata": {},
@@ -459,7 +489,7 @@
459489
"name": "python",
460490
"nbconvert_exporter": "python",
461491
"pygments_lexer": "ipython3",
462-
"version": "3.6.7"
492+
"version": "3.7.1"
463493
}
464494
},
465495
"nbformat": 4,

doc/references.bib

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@article{perez2011python,
2+
author = {Pérez, Fernando and Granger, Brian E. and Hunter, John D.},
3+
title = {Python: An Ecosystem for Scientific Computing},
4+
journal = {Computing in Science Engineering},
5+
volume = 13,
6+
number = 2,
7+
pages = {13--21},
8+
doi = {10.1109/MCSE.2010.119},
9+
year = 2011,
10+
}
11+
12+
@incollection{kluyver2016jupyter,
13+
author = {Kluyver, Thomas and Ragan-Kelley, Benjamin and Pérez, Fernando and Granger, Brian and Bussonnier, Matthias and Frederic, Jonathan and Kelley, Kyle and Hamrick, Jessica and Grout, Jason and Corlay, Sylvain and Ivanov, Paul and Avila, Damián and Abdalla, Safia and Willing, Carol and {Jupyter Development Team}},
14+
title = {{Jupyter Notebooks}---a publishing format for reproducible computational workflows},
15+
booktitle = {Positioning and Power in Academic Publishing: Players, Agents and Agendas},
16+
pages = {87--90},
17+
publisher = {IOS Press},
18+
doi = {10.3233/978-1-61499-649-1-87},
19+
editor = {Loizides, Fernando and Schmidt, Birgit},
20+
year = 2016,
21+
}

src/nbsphinx.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
import subprocess
3333
try:
3434
from urllib.parse import unquote # Python 3.x
35+
from html.parser import HTMLParser
3536
except ImportError:
3637
from urllib2 import unquote # Python 2.x
38+
from HTMLParser import HTMLParser
3739

3840
import docutils
3941
from docutils.parsers import rst
@@ -1017,6 +1019,32 @@ def convert_pandoc(text, from_format, to_format):
10171019
return markdown2rst(text)
10181020

10191021

1022+
class CitationParser(HTMLParser):
1023+
1024+
def handle_starttag(self, tag, attrs):
1025+
if self._check_cite(attrs):
1026+
self.starttag = tag
1027+
1028+
def handle_endtag(self, tag):
1029+
self.endtag = tag
1030+
1031+
def handle_startendtag(self, tag, attrs):
1032+
self._check_cite(attrs)
1033+
1034+
def _check_cite(self, attrs):
1035+
for name, value in attrs:
1036+
if name == 'data-cite':
1037+
self.cite = ':cite:`' + value + '`'
1038+
return True
1039+
return False
1040+
1041+
def reset(self):
1042+
HTMLParser.reset(self)
1043+
self.starttag = ''
1044+
self.endtag = ''
1045+
self.cite = ''
1046+
1047+
10201048
def markdown2rst(text):
10211049
"""Convert a Markdown string to reST via pandoc.
10221050
@@ -1041,16 +1069,38 @@ def displaymath(text):
10411069
]
10421070
}
10431071

1044-
def rawlatex2math_hook(obj):
1072+
def parse_html(obj):
1073+
p = CitationParser()
1074+
p.feed(obj['c'][1])
1075+
p.close()
1076+
return p
1077+
1078+
open_cite_tag = ''
1079+
1080+
def object_hook(obj):
1081+
nonlocal open_cite_tag
1082+
if open_cite_tag:
1083+
if obj.get('t') == 'RawInline' and obj['c'][0] == 'html':
1084+
p = parse_html(obj)
1085+
if p.endtag == open_cite_tag:
1086+
open_cite_tag = ''
1087+
return {'t': 'Str', 'c': ''} # Object is replaced by empty string
1088+
10451089
if obj.get('t') == 'RawBlock' and obj['c'][0] == 'latex':
10461090
obj['t'] = 'Para'
10471091
obj['c'] = [displaymath(obj['c'][1])]
10481092
elif obj.get('t') == 'RawInline' and obj['c'][0] == 'tex':
10491093
obj = displaymath(obj['c'][1])
1094+
elif obj.get('t') == 'RawInline' and obj['c'][0] == 'html':
1095+
p = parse_html(obj)
1096+
if p.starttag:
1097+
open_cite_tag = p.starttag
1098+
if p.cite:
1099+
obj = {'t': 'RawInline', 'c': ['rst', p.cite]}
10501100
return obj
10511101

1052-
def rawlatex2math(text):
1053-
json_data = json.loads(text, object_hook=rawlatex2math_hook)
1102+
def filter_func(text):
1103+
json_data = json.loads(text, object_hook=object_hook)
10541104
return json.dumps(json_data)
10551105

10561106
input_format = 'markdown'
@@ -1059,7 +1109,7 @@ def rawlatex2math(text):
10591109
if nbconvert.utils.version.check_version(v, '1.13'):
10601110
input_format += '-native_divs+raw_html'
10611111

1062-
rststring = pandoc(text, input_format, 'rst', filter_func=rawlatex2math)
1112+
rststring = pandoc(text, input_format, 'rst', filter_func=filter_func)
10631113
return re.sub(r'^\n( *)\x0e:nowrap:\x0f$',
10641114
r'\1:nowrap:',
10651115
rststring,

0 commit comments

Comments
 (0)