|
4 | 4 | """ |
5 | 5 |
|
6 | 6 | import itertools |
| 7 | +from pathlib import Path |
| 8 | + |
7 | 9 | from sphinx_gallery.sorting import ExplicitOrder |
8 | 10 |
|
9 | 11 | # Gallery sections shall be displayed in the following order. |
@@ -81,70 +83,87 @@ def __call__(self, item): |
81 | 83 | else: |
82 | 84 | return f"{self.ordered_list.index(UNSORTED):04d}{item}" |
83 | 85 |
|
84 | | -# Subsection order: |
85 | | -# Subsections are ordered by filename, unless they appear in the following |
86 | | -# lists in which case the list order determines the order within the section. |
87 | | -# Examples/tutorials that do not appear in a list will be appended. |
88 | | - |
89 | | -list_all = [ |
90 | | - # **Tutorials** |
91 | | - # introductory |
92 | | - "quick_start", "pyplot", "images", "lifecycle", "customizing", |
93 | | - # intermediate |
94 | | - "artists", "legend_guide", "color_cycle", |
95 | | - "constrainedlayout_guide", "tight_layout_guide", |
96 | | - # advanced |
97 | | - # text |
98 | | - "text_intro", "text_props", |
99 | | - # colors |
100 | | - "colors", |
101 | | - |
102 | | - # **Examples** |
103 | | - # animation |
104 | | - "simple_anim", # Most basic example |
105 | | - # color |
106 | | - "color_demo", |
107 | | - # pies |
108 | | - "pie_features", "pie_demo2", |
109 | | - # scales |
110 | | - "scales", # Scales overview |
111 | | - |
112 | | - # **Plot Types |
113 | | - # Basic |
114 | | - "plot", "scatter_plot", "bar", "stem", "step", "fill_between", |
115 | | - # Arrays |
116 | | - "imshow", "pcolormesh", "contour", "contourf", |
117 | | - "barbs", "quiver", "streamplot", |
118 | | - # Stats |
119 | | - "hist_plot", "boxplot_plot", "errorbar_plot", "violin", |
120 | | - "eventplot", "hist2d", "hexbin", "pie", |
121 | | - # Unstructured |
122 | | - "tricontour", "tricontourf", "tripcolor", "triplot", |
123 | | - # Spines |
124 | | - "spines", "spine_placement_demo", "spines_dropped", |
125 | | - "multiple_yaxis_with_spines", "centered_spines_with_arrows", |
126 | | - ] |
127 | | -explicit_subsection_order = [item + ".py" for item in list_all] |
128 | | - |
129 | | - |
130 | | -class MplExplicitSubOrder(ExplicitOrder): |
131 | | - """For use within the 'within_subsection_order' key.""" |
| 86 | + |
| 87 | +class MplFileExplicitOrder(ExplicitOrder): |
| 88 | + """ |
| 89 | + An explicit order class that reads the order of examples from 'gallery_order.txt'. |
| 90 | +
|
| 91 | + For use with the sphinx_gallery 'within_subsection_order' key. |
| 92 | +
|
| 93 | + The file contains a list of example filenames (without the .py extension) in the |
| 94 | + desired order, with an optional '*' to indicate where not-listed examples should be |
| 95 | + placed. |
| 96 | +
|
| 97 | + If '*' is not present, all examples must be listed, or an error will be raised. |
| 98 | + Use this if you want to ensure that a full order is intentionally maintained. |
| 99 | + """ |
132 | 100 | def __init__(self, src_dir): |
133 | | - self.src_dir = src_dir # src_dir is unused here |
134 | | - self.ordered_list = explicit_subsection_order |
| 101 | + ordered_list = self.read_gallery_order(Path(src_dir)) or [] |
| 102 | + super().__init__(ordered_list) |
| 103 | + |
| 104 | + @staticmethod |
| 105 | + def read_gallery_order(src_dir: Path): |
| 106 | + """Return the list of examples to be sorted; read from 'gallery_order.txt'.""" |
| 107 | + gallery_order_txt = src_dir / "gallery_order.txt" |
| 108 | + if not gallery_order_txt.exists(): |
| 109 | + return None |
| 110 | + lines = [ |
| 111 | + line.strip() |
| 112 | + for line in gallery_order_txt.read_text().splitlines() |
| 113 | + if line.strip() and not line.startswith("#") |
| 114 | + ] |
| 115 | + |
| 116 | + try: |
| 117 | + placeholder_index = lines.index("*") |
| 118 | + except ValueError: |
| 119 | + placeholder_index = None |
| 120 | + |
| 121 | + lines = [line + ".py" for line in lines] |
| 122 | + |
| 123 | + if placeholder_index is None: |
| 124 | + front = lines |
| 125 | + back = [] |
| 126 | + else: |
| 127 | + front = lines[:placeholder_index] |
| 128 | + back = lines[placeholder_index+1:] |
| 129 | + |
| 130 | + listed_examples = {*front, *back} |
| 131 | + existing_examples = { |
| 132 | + file.name for file in src_dir.iterdir() if file.suffix == ".py" |
| 133 | + } |
| 134 | + |
| 135 | + non_existing_examples = listed_examples - existing_examples |
| 136 | + missing_examples = existing_examples - listed_examples |
| 137 | + print(f"non_existing_examples: {non_existing_examples}") |
| 138 | + print(f"missing_examples: {missing_examples}") |
| 139 | + |
| 140 | + if non_existing_examples: |
| 141 | + raise ValueError( |
| 142 | + f"The following examples listed in {gallery_order_txt} do not exist: " |
| 143 | + f"{', '.join(non_existing_examples)}") |
| 144 | + if placeholder_index is None and missing_examples: |
| 145 | + raise ValueError( |
| 146 | + f"The following examples are not listed in {gallery_order_txt}. " |
| 147 | + f"Either include them or add a '*' to indicate where not listed " |
| 148 | + f"examples should be placed: " |
| 149 | + f"{', '.join(missing_examples)}" |
| 150 | + ) |
| 151 | + |
| 152 | + mid = list(sorted(missing_examples)) |
| 153 | + return front + mid + back |
135 | 154 |
|
136 | 155 | def __call__(self, item): |
137 | 156 | """Return a string determining the sort order.""" |
138 | | - if item in self.ordered_list: |
139 | | - return f"{self.ordered_list.index(item):04d}" |
140 | | - else: |
141 | | - # ensure not explicitly listed items come last. |
142 | | - return "zzz" + item |
| 157 | + if not self.ordered_list: |
| 158 | + return item |
| 159 | + return f"{self.ordered_list.index(item):04d}" |
143 | 160 |
|
| 161 | + def __repr__(self): |
| 162 | + return '<%s: %s>' % (self.__class__.__name__, self.ordered_list) |
144 | 163 |
|
145 | 164 | # Provide the above classes for use in conf.py |
146 | 165 | sectionorder = MplExplicitOrder(explicit_order_folders) |
147 | | -subsectionorder = MplExplicitSubOrder |
| 166 | +subsectionorder = MplFileExplicitOrder |
148 | 167 |
|
149 | 168 | _preserve_count = itertools.count() |
150 | 169 |
|
|
0 commit comments