Skip to content

Commit 0d57c23

Browse files
committed
Merge pull request scikit-learn#3327 from jnothman/examples_in_apiref
DOC show referring examples on API reference pages
2 parents 96b6642 + 927de5e commit 0d57c23

File tree

8 files changed

+228
-171
lines changed

8 files changed

+228
-171
lines changed

doc/conf.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
# documentation.
124124
html_theme_options = {'oldversion': False, 'collapsiblesidebar': True,
125125
'google_analytics': True, 'surveybanner': False,
126-
'sprintbanner' : True}
126+
'sprintbanner': True}
127127

128128
# Add any paths that contain custom themes here, relative to this directory.
129129
html_theme_path = ['themes']
@@ -225,7 +225,9 @@
225225

226226
trim_doctests_flags = True
227227

228-
# Add the 'copybutton' javascript, to hide/show the prompt in code
229-
# examples
228+
230229
def setup(app):
230+
# to hide/show the prompt in code examples:
231231
app.add_javascript('js/copybutton.js')
232+
# to format example galleries:
233+
app.add_javascript('js/examples.js')

doc/sphinxext/gen_rst.py

Lines changed: 81 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import gzip
2020
import posixpath
2121
import subprocess
22+
from textwrap import dedent
2223

2324

2425
# Try Python 2 first, otherwise load from Python 3
@@ -489,141 +490,26 @@ def generate_example_rst(app):
489490
490491
491492
<style type="text/css">
492-
493493
div#sidebarbutton {
494-
display: none;
495-
}
496-
497-
.figure {
498-
float: left;
499-
margin: 10px;
500-
-webkit-border-radius: 10px; /* Saf3-4, iOS 1-3.2, Android <1.6 */
501-
-moz-border-radius: 10px; /* FF1-3.6 */
502-
border-radius: 10px; /* Opera 10.5, IE9, Saf5, Chrome, FF4, iOS 4, Android 2.1+ */
503-
border: 2px solid #fff;
504-
background-color: white;
505-
/* --> Thumbnail image size */
506-
width: 150px;
507-
height: 100px;
508-
-webkit-background-size: 150px 100px; /* Saf3-4 */
509-
-moz-background-size: 150px 100px; /* FF3.6 */
510-
}
511-
512-
.figure img {
513-
display: inline;
514-
}
515-
516-
div.docstringWrapper p.caption {
517-
display: block;
518-
-webkit-box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.0);
519-
-moz-box-shadow: 0px 0px 20px rgba(0, 0, 0, .0); /* FF3.5 - 3.6 */
520-
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.0); /* Opera 10.5, IE9, FF4+, Chrome 10+ */
521-
padding: 0px;
522-
border: white;
523-
}
524-
525-
div.docstringWrapper p {
526-
display: none;
527-
background-color: white;
528-
-webkit-box-shadow: 0px 0px 20px rgba(0, 0, 0, 1.00);
529-
-moz-box-shadow: 0px 0px 20px rgba(0, 0, 0, 1.00); /* FF3.5 - 3.6 */
530-
box-shadow: 0px 0px 20px rgba(0, 0, 0, 1.00); /* Opera 10.5, IE9, FF4+, Chrome 10+ */
531-
padding: 13px;
532-
margin-top: 0px;
533-
border-style: solid;
534-
border-width: 1px;
494+
/* hide the sidebar collapser, while ensuring vertical arrangement */
495+
width: 0px;
496+
overflow: hidden;
535497
}
536-
537-
538498
</style>
539499
540500
541-
.. raw:: html
542-
543-
544-
<script type="text/javascript">
545-
546-
function animateClone(e){
547-
var position;
548-
position = $(this).position();
549-
var clone = $(this).closest('.thumbnailContainer').find('.clonedItem');
550-
var clone_fig = clone.find('.figure');
551-
clone.css("left", position.left - 70).css("top", position.top - 70).css("position", "absolute").css("z-index", 1000).css("background-color", "white");
552-
553-
var cloneImg = clone_fig.find('img');
554-
555-
clone.show();
556-
clone.animate({
557-
height: "270px",
558-
width: "320px"
559-
}, 0
560-
);
561-
cloneImg.css({
562-
'max-height': "200px",
563-
'max-width': "280px"
564-
});
565-
cloneImg.animate({
566-
height: "200px",
567-
width: "280px"
568-
}, 0
569-
);
570-
clone_fig.css({
571-
'margin-top': '20px',
572-
});
573-
clone_fig.show();
574-
clone.find('p').css("display", "block");
575-
clone_fig.css({
576-
height: "240",
577-
width: "305px"
578-
});
579-
cloneP_height = clone.find('p.caption').height();
580-
clone_fig.animate({
581-
height: (200 + cloneP_height)
582-
}, 0
583-
);
584-
585-
clone.bind("mouseleave", function(e){
586-
clone.animate({
587-
height: "100px",
588-
width: "150px"
589-
}, 10, function(){$(this).hide();});
590-
clone_fig.animate({
591-
height: "100px",
592-
width: "150px"
593-
}, 10, function(){$(this).hide();});
594-
});
595-
} //end animateClone()
596-
597-
598-
$(window).load(function () {
599-
$(".figure").css("z-index", 1);
600-
601-
$(".docstringWrapper").each(function(i, obj){
602-
var clone;
603-
var $obj = $(obj);
604-
clone = $obj.clone();
605-
clone.addClass("clonedItem");
606-
clone.appendTo($obj.closest(".thumbnailContainer"));
607-
clone.hide();
608-
$obj.bind("mouseenter", animateClone);
609-
}); // end each
610-
}); // end
611-
612-
</script>
613-
614-
615-
616501
Examples
617502
========
618503
619504
.. _examples-index:
620505
""")
621506
# Here we don't use an os.walk, but we recurse only twice: flat is
622507
# better than nested.
623-
generate_dir_rst('.', fhindex, example_dir, root_dir, plot_gallery)
508+
seen_backrefs = set()
509+
generate_dir_rst('.', fhindex, example_dir, root_dir, plot_gallery, seen_backrefs)
624510
for dir in sorted(os.listdir(example_dir)):
625511
if os.path.isdir(os.path.join(example_dir, dir)):
626-
generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery)
512+
generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery, seen_backrefs)
627513
fhindex.flush()
628514

629515

@@ -665,7 +551,47 @@ def line_count_sort(file_list, target_dir):
665551
return np.array(unsorted[index][:, 0]).tolist()
666552

667553

668-
def generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery):
554+
def _thumbnail_div(subdir, full_dir, fname, snippet):
555+
"""Generates RST to place a thumbnail in a gallery"""
556+
thumb = os.path.join(full_dir, 'images', 'thumb', fname[:-3] + '.png')
557+
link_name = os.path.join(full_dir, fname).replace(os.path.sep, '_')
558+
ref_name = os.path.join(subdir, fname).replace(os.path.sep, '_')
559+
if ref_name.startswith('._'):
560+
ref_name = ref_name[2:]
561+
out = []
562+
out.append("""
563+
564+
.. raw:: html
565+
566+
567+
<div class="thumbnailContainer">
568+
<div class="docstringWrapper">
569+
570+
571+
""")
572+
573+
out.append('.. figure:: %s\n' % thumb)
574+
if link_name.startswith('._'):
575+
link_name = link_name[2:]
576+
if full_dir != '.':
577+
out.append(' :target: ./%s/%s.html\n\n' % (full_dir, fname[:-3]))
578+
else:
579+
out.append(' :target: ./%s.html\n\n' % link_name[:-3])
580+
out.append(""" :ref:`example_%s`
581+
582+
583+
.. raw:: html
584+
585+
586+
<p>%s
587+
</p></div>
588+
</div>
589+
590+
""" % (ref_name, snippet))
591+
return ''.join(out)
592+
593+
594+
def generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery, seen_backrefs):
669595
""" Generate the rst file for an example directory.
670596
"""
671597
if not dir == '.':
@@ -677,7 +603,7 @@ def generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery):
677603
if not os.path.exists(os.path.join(src_dir, 'README.txt')):
678604
print(80 * '_')
679605
print('Example directory %s does not have a README.txt file' %
680-
src_dir)
606+
src_dir)
681607
print('Skipping this directory')
682608
print(80 * '_')
683609
return
@@ -697,51 +623,36 @@ def generate_dir_rst(dir, fhindex, example_dir, root_dir, plot_gallery):
697623
os.makedirs(os.path.join(dir, 'images', 'thumb'))
698624
for fname in sorted_listdir:
699625
if fname.endswith('py'):
700-
generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery)
626+
backrefs = generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery)
701627
new_fname = os.path.join(src_dir, fname)
702-
_, fdocstring, _ = extract_docstring(new_fname, True)
703-
thumb = os.path.join(dir, 'images', 'thumb', fname[:-3] + '.png')
704-
link_name = os.path.join(dir, fname).replace(os.path.sep, '_')
628+
_, snippet, _ = extract_docstring(new_fname, True)
629+
fhindex.write(_thumbnail_div(dir, dir, fname, snippet))
705630
fhindex.write("""
706631
707-
.. raw:: html
708-
709-
710-
<div class="thumbnailContainer">
711-
<div class="docstringWrapper">
712-
713-
714-
""")
715-
716-
fhindex.write('.. figure:: %s\n' % thumb)
717-
if link_name.startswith('._'):
718-
link_name = link_name[2:]
719-
if dir != '.':
720-
fhindex.write(' :target: ./%s/%s.html\n\n' % (dir,
721-
fname[:-3]))
722-
else:
723-
fhindex.write(' :target: ./%s.html\n\n' % link_name[:-3])
724-
fhindex.write(""" :ref:`example_%s`
725-
726-
727-
.. raw:: html
728-
729-
730-
<p>%s
731-
</p></div>
732-
</div>
733-
734-
735632
.. toctree::
736633
:hidden:
737634
738635
%s/%s
739636
740-
""" % (link_name, fdocstring, dir, fname[:-3]))
637+
""" % (dir, fname[:-3]))
638+
for backref in backrefs:
639+
include_path = os.path.join(root_dir, '../modules/generated/%s.examples' % backref)
640+
seen = backref in seen_backrefs
641+
with open(include_path, 'a' if seen else 'w') as ex_file:
642+
if not seen:
643+
# heading
644+
print(file=ex_file)
645+
print('Examples using ``%s``' % backref, file=ex_file)
646+
print('-----------------%s--' % ('-' * len(backref)),
647+
file=ex_file)
648+
print(file=ex_file)
649+
rel_dir = os.path.join('../../auto_examples', dir)
650+
ex_file.write(_thumbnail_div(dir, rel_dir, fname, snippet))
651+
seen_backrefs.add(backref)
741652
fhindex.write("""
742653
.. raw:: html
743654
744-
<div style="clear: both"></div>
655+
<div class="clearer"></div>
745656
""") # clear at the end of the section
746657

747658
# modules for which we embed links into example code
@@ -881,6 +792,8 @@ def identify_names(code):
881792

882793
def generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery):
883794
""" Generate the rst file for a given example.
795+
796+
Returns the set of sklearn functions/classes imported in the example.
884797
"""
885798
base_image_name = os.path.splitext(fname)[0]
886799
image_fname = '%s_%%03d.png' % base_image_name
@@ -969,13 +882,13 @@ def generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery):
969882
# incrementally: 1, 2, 3 and not 1, 2, 5)
970883
# * iterate over [fig_mngr.num for fig_mngr in
971884
# matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
972-
for fig_num in (fig_mngr.num for fig_mngr in
973-
matplotlib._pylab_helpers.Gcf.get_all_fig_managers()):
885+
fig_managers = matplotlib._pylab_helpers.Gcf.get_all_fig_managers()
886+
for fig_mngr in fig_managers:
974887
# Set the fig_num figure as the current figure as we can't
975888
# save a figure that's not the current figure.
976-
plt.figure(fig_num)
977-
plt.savefig(image_path % fig_num)
978-
figure_list.append(image_fname % fig_num)
889+
plt.figure(fig_mngr.num)
890+
plt.savefig(image_path % fig_mngr.num)
891+
figure_list.append(image_fname % fig_mngr.num)
979892
except:
980893
print(80 * '_')
981894
print('%s is not compiling:' % fname)
@@ -994,7 +907,7 @@ def generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery):
994907

995908
# generate thumb file
996909
this_template = plot_rst_template
997-
car_thumb_path = os.path.join(os.path.split(root_dir)[0], '_build/html/stable/_images/')
910+
car_thumb_path = os.path.join(os.path.split(root_dir)[0], '_build/html/stable/_images/')
998911
# Note: normaly, make_thumbnail is used to write to the path contained in `thumb_file`
999912
# which is within `auto_examples/../images/thumbs` depending on the example.
1000913
# Because the carousel has different dimensions than those of the examples gallery,
@@ -1045,6 +958,11 @@ def generate_file_rst(fname, target_dir, src_dir, root_dir, plot_gallery):
1045958
with open(codeobj_fname, 'wb') as fid:
1046959
pickle.dump(example_code_obj, fid, pickle.HIGHEST_PROTOCOL)
1047960

961+
backrefs = set('{module_short}.{name}'.format(**entry)
962+
for entry in example_code_obj.values()
963+
if entry['module'].startswith('sklearn'))
964+
return backrefs
965+
1048966

1049967
def embed_code_links(app, exception):
1050968
"""Embed hyperlinks to documentation into example code"""

doc/templates/class.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@
99
.. automethod:: __init__
1010
{% endblock %}
1111

12+
.. include:: {{module}}.{{objname}}.examples
1213

14+
.. raw:: html
15+
16+
<div class="clearer"></div>

doc/templates/class_with_call.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@
1010
.. automethod:: __call__
1111
{% endblock %}
1212

13+
.. include:: {{module}}.{{objname}}.examples
1314

15+
.. raw:: html
16+
17+
<div class="clearer"></div>

doc/templates/function.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@
55

66
.. autofunction:: {{ objname }}
77

8+
.. include:: {{module}}.{{objname}}.examples
89

10+
.. raw:: html
11+
12+
<div class="clearer"></div>

doc/themes/scikit-learn/static/css/examples.css

Whitespace-only changes.

0 commit comments

Comments
 (0)