-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathconf.py
More file actions
637 lines (532 loc) · 21.4 KB
/
conf.py
File metadata and controls
637 lines (532 loc) · 21.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# For autodoc compilation see:
# https://medium.com/@eikonomega/getting-started-with-sphinx-autodoc-part-1-2cebbbca5365
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Imports and paths --------------------------------------------------------------
# Import statements
import datetime
import logging
import os
import re
import subprocess
import sys
# Surpress warnings from cartopy when downloading data inside docs env
import warnings
from pathlib import Path
try:
from cartopy.io import DownloadWarning
warnings.filterwarnings("ignore", category=DownloadWarning)
except ImportError:
# In case cartopy isn't installed yet when conf.py is executed
pass
# Handle sphinx.util.console deprecation
# Note this has been deprecated in Sphinx 5.0 and some extensions still use the console module. Needs to be updated later
try:
# For newer Sphinx versions where sphinx.util.console is removed
import sphinx
if not hasattr(sphinx.util, "console"):
# Create a compatibility layer
import sys
import sphinx.util
from sphinx.util import logging
class ConsoleColorFallback:
def __getattr__(self, name):
return (
lambda text: text
) # Return a function that returns the text unchanged
sphinx.util.console = ConsoleColorFallback()
except Exception:
pass
# Build what's news page from github releases
from subprocess import run
FAST_PREVIEW = os.environ.get("UPLT_DOCS_FAST_PREVIEW", "").strip().lower() in {
"1",
"true",
"yes",
"on",
}
if not FAST_PREVIEW:
run([sys.executable, "_scripts/fetch_releases.py"], check=False)
# Docs theme selector. Default to Shibuya, but keep env override for A/B checks.
DOCS_THEME = os.environ.get("UPLT_DOCS_THEME", "shibuya").strip().lower()
if DOCS_THEME in {"ultratheme", "rtd", "sphinx_rtd_light_dark"}:
DOCS_THEME = "sphinx_rtd_light_dark"
else:
DOCS_THEME = "shibuya"
if DOCS_THEME == "shibuya":
try:
import shibuya # noqa: F401
except Exception:
print("Shibuya theme not installed; falling back to sphinx_rtd_light_dark.")
DOCS_THEME = "sphinx_rtd_light_dark"
# Update path for sphinx-automodapi and sphinxext extension
sys.path.append(os.path.abspath("."))
sys.path.insert(0, os.path.abspath(".."))
_ultratheme_path = os.path.abspath("../UltraTheme")
if os.path.isdir(_ultratheme_path):
sys.path.insert(0, _ultratheme_path)
try:
import ultraplot_theme # noqa: F401
HAVE_ULTRAPLOT_THEME_EXT = True
except Exception:
HAVE_ULTRAPLOT_THEME_EXT = False
# Ensure whats_new exists during local builds without GitHub fetch.
whats_new_path = Path(__file__).parent / "whats_new.rst"
if not whats_new_path.exists() or not whats_new_path.read_text().strip():
whats_new_path.write_text(
".. _whats_new:\n\nWhat's New\n==========\n\n"
"Release notes are generated during the docs build.\n"
)
# Avoid concatenating matplotlib docstrings to reduce docutils parsing issues.
try:
import matplotlib as mpl
mpl.rcParams["docstring.hardcopy"] = True
except Exception:
pass
# Silence font discovery warnings like "findfont: Font family ..."
for _logger_name in ("matplotlib", "matplotlib.font_manager"):
_logger = logging.getLogger(_logger_name)
_logger.setLevel(logging.ERROR)
_logger.propagate = False
# Suppress deprecated rc key warnings from local configs during docs builds.
try:
from ultraplot.internals.warnings import UltraPlotWarning
warnings.filterwarnings("ignore")
warnings.filterwarnings(
"ignore",
category=UltraPlotWarning,
)
except Exception:
pass
# Print available system fonts
from matplotlib.font_manager import fontManager
from sphinx_gallery.sorting import ExplicitOrder, FileNameSortKey
def _set_plot_transparency_defaults():
"""
Use transparent defaults so rendered docs figures adapt to light/dark themes.
"""
try:
import matplotlib as mpl
except Exception:
return
mpl.rcParams["figure.facecolor"] = "none"
mpl.rcParams["axes.facecolor"] = "none"
mpl.rcParams["savefig.facecolor"] = "none"
mpl.rcParams["savefig.edgecolor"] = "none"
def _reset_ultraplot(gallery_conf, fname):
"""
Reset UltraPlot rc state between gallery examples.
"""
try:
import ultraplot as uplt
except Exception:
return
for _logger_name in ("matplotlib", "matplotlib.font_manager"):
_logger = logging.getLogger(_logger_name)
_logger.setLevel(logging.ERROR)
_logger.propagate = False
uplt.rc.reset()
_set_plot_transparency_defaults()
_set_plot_transparency_defaults()
# -- Project information -------------------------------------------------------
# The basic info
project = "UltraPlot"
copyright = f"{datetime.datetime.today().year}, UltraPlot"
author = "Luke L. B. Davis"
# The short X.Y version
version = ""
# The full version, including alpha/beta/rc tags
release = ""
# Faster builds
parallel_read_safe = True
parallel_write_safe = True
# -- Create files --------------------------------------------------------------
# Create RST table and sample ultraplotrc file
from ultraplot.config import rc
folder = (Path(__file__).parent / "_static").absolute()
if not folder.is_dir():
folder.mkdir()
rc._save_rst(str(folder / "rctable.rst"))
rc._save_yaml(str(folder / "ultraplotrc"))
# -- Setup basemap --------------------------------------------------------------
# Hack to get basemap to work
# See: https://github.com/readthedocs/readthedocs.org/issues/5339
if os.environ.get("READTHEDOCS", None) == "True":
conda = (
Path(os.environ["CONDA_ENVS_PATH"]) / os.environ["CONDA_DEFAULT_ENV"]
).absolute()
else:
conda = Path(os.environ["CONDA_PREFIX"]).absolute()
os.environ["GEOS_DIR"] = str(conda)
os.environ["PROJ_LIB"] = str((conda / "share" / "proj"))
# Install basemap if does not exist
# Extremely ugly but impossible to install in environment.yml. Must set
# GEOS_DIR before installing so cannot install with pip and basemap conflicts
# with conda > 0.19 so cannot install with conda in environment.yml.
try:
import mpl_toolkits.basemap # noqa: F401
except ImportError:
subprocess.check_call(
["pip", "install", "basemap"]
# ["pip", "install", "git+https://github.com/matplotlib/basemap@v1.2.2rel"]
)
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
"matplotlib.sphinxext.plot_directive", # see: https://matplotlib.org/sampledoc/extensions.html # noqa: E501
"sphinx.ext.autodoc", # include documentation from docstrings
"sphinx_design",
"sphinx.ext.doctest", # >>> examples
"sphinx.ext.extlinks", # for :pr:, :issue:, :commit:
"sphinx.ext.autosectionlabel", # use :ref:`Heading` for any heading
"sphinx.ext.todo", # Todo headers and todo:: directives
"sphinx.ext.mathjax", # LaTeX style math
"sphinx.ext.viewcode", # view code links
"sphinx.ext.napoleon", # for NumPy style docstrings
"sphinx.ext.intersphinx", # external links
"sphinx.ext.autosummary", # autosummary directive
"sphinxext.custom_roles", # local extension
"sphinx_automodapi.automodapi", # fork of automodapi
"sphinx_copybutton", # add copy button to code
"_ext.notoc",
"nbsphinx", # parse rst books
"sphinx_gallery.gen_gallery",
]
if not FAST_PREVIEW:
extensions.append("sphinx_sitemap")
if HAVE_ULTRAPLOT_THEME_EXT:
extensions.append("ultraplot_theme")
elif DOCS_THEME == "sphinx_rtd_light_dark":
extensions.append("sphinx_rtd_light_dark")
autosectionlabel_prefix_document = True
# The master toctree document.
master_doc = "index"
# The suffix(es) of source filenames, either a string or list.
source_suffix = [".rst", ".html"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# List of file patterns relative to source dir that should be ignored
exclude_patterns = [
"conf.py",
"sphinxext",
"_build",
"_scripts",
"_templates",
"_themes",
"*.ipynb",
"gallery/**/*.codeobj.json",
"gallery/**/*.ipynb",
"gallery/**/*.md5",
"gallery/**/*.py",
"gallery/**/*.zip",
"**.ipynb_checkpoints" ".DS_Store",
"trash",
"tmp",
]
suppress_warnings = [
"docutils",
"nbsphinx.notebooktitle",
"toc.not_included",
"toc.not_readable",
"autosectionlabel.*",
"autosectionlabel",
]
autodoc_default_options = {
"private-members": False,
"special-members": False,
"undoc-members": False,
}
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
language = "en"
# Role. Default family is py but can also set default role so don't need
# :func:`name`, :module:`name`, etc.
default_role = "py:obj"
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = False # UltraPlot imports everything in top-level namespace
# Autodoc configuration. Here we concatenate class and __init__ docstrings
# See: http://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
autoclass_content = "both" # options are 'class', 'both', 'init'
# Generate stub pages whenever ::autosummary directive encountered
# This way don't have to call sphinx-autogen manually
autosummary_generate = True
# Automodapi tool: https://sphinx-automodapi.readthedocs.io/en/latest/automodapi.html
# Normally have to *enumerate* function names manually. This will document them
# automatically. Just be careful to exclude public names from automodapi:: directive.
automodapi_toctreedirnm = "api"
automodsumm_inherited_members = False
# Doctest configuration. For now do not run tests, they are just to show syntax
# and expected output may be graphical
doctest_test_doctest_blocks = ""
# Cupybutton configuration
# See: https://sphinx-copybutton.readthedocs.io/en/latest/
copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: "
copybutton_prompt_is_regexp = True
copybutton_only_copy_prompt_lines = True
copybutton_remove_prompts = True
# Links for What's New github commits, issues, and pull requests
extlinks = {
"issue": ("https://github.com/ultraplot/ultraplot/issues/%s", "GH#%s"),
"commit": ("https://github.com/Ultraplot/ultraplot/commit/%s", "@%s"),
"pr": ("https://github.com/Ultraplot/ultraplot/pull/%s", "GH#%s"),
}
# Set up mapping for other projects' docs
intersphinx_mapping = {
"cycler": ("https://matplotlib.org/cycler/", None),
"matplotlib": ("https://matplotlib.org/stable/", None),
"sphinx": ("https://www.sphinx-doc.org/en/master/", None),
"python": ("https://docs.python.org/3/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
"xarray": ("https://docs.xarray.dev/en/stable/", None),
"cartopy": ("https://cartopy.readthedocs.io/stable/", None),
"basemap": ("https://matplotlib.org/basemap/stable/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
"pint": ("https://pint.readthedocs.io/en/stable/", None),
"networkx": ("https://networkx.org/documentation/stable/", None),
}
if FAST_PREVIEW:
intersphinx_mapping = {}
# Fix duplicate class member documentation from autosummary + numpydoc
# See: https://github.com/phn/pytpm/issues/3#issuecomment-12133978
numpydoc_show_class_members = False
# Napoleon options
# See: http://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html
# * use_param is set to False so that we can put multiple "parameters"
# on one line -- for example 'xlocator, ylocator : locator-spec, optional'
# * docs claim napoleon_preprocess_types and napoleon_type_aliases only work
# when napoleon_use_param is True but xarray sets to False and it still works
# * use_keyword is set to False because we do not want separate 'Keyword Arguments'
# section and have same issue for multiple keywords.
# * use_ivar and use_rtype are set to False for (presumably) style consistency
# with the above options set to False.
napoleon_use_ivar = False
napoleon_use_param = False
napoleon_use_keyword = False
napoleon_use_rtype = False
napoleon_numpy_docstring = True
napoleon_google_docstring = False
napoleon_include_init_with_doc = False # move init doc to 'class' doc
napoleon_preprocess_types = True
napoleon_type_aliases = {
# Python or inherited terms
# NOTE: built-in types are automatically included
"callable": ":py:func:`callable`",
"sequence": ":term:`sequence`",
"dict-like": ":term:`dict-like <mapping>`",
"path-like": ":term:`path-like <path-like object>`",
"array-like": ":term:`array-like <array_like>`",
# UltraPlot defined terms
"unit-spec": ":py:func:`unit-spec <ultraplot.utils.units>`",
"locator-spec": ":py:func:`locator-spec <ultraplot.constructor.Locator>`",
"formatter-spec": ":py:func:`formatter-spec <ultraplot.constructor.Formatter>`",
"scale-spec": ":py:func:`scale-spec <ultraplot.constructor.Scale>`",
"colormap-spec": ":py:func:`colormap-spec <ultraplot.constructor.Colormap>`",
"cycle-spec": ":py:func:`cycle-spec <ultraplot.constructor.Cycle>`",
"norm-spec": ":py:func:`norm-spec <ultraplot.constructor.Norm>`",
"color-spec": ":py:func:`color-spec <matplotlib.colors.is_color_like>`",
"artist": ":py:func:`artist <matplotlib.artist.Artist>`",
}
# Keep autodoc aliases stable across builds so incremental Sphinx caching works.
autodoc_type_aliases = dict(napoleon_type_aliases)
# Fail on error. Note nbsphinx compiles all notebooks in docs unless excluded
nbsphinx_allow_errors = False
# Give *lots* of time for cell execution
nbsphinx_timeout = 300
# Add jupytext support to nbsphinx with conversion cache.
nbsphinx_custom_formats = {
".py": ["sphinxext.jupytext_cache.reads_cached", {"fmt": "py:percent"}]
}
# Keep notebook output backgrounds theme-adaptive.
nbsphinx_execute_arguments = [
"--InlineBackend.rc={"
"'figure.facecolor': 'none', "
"'axes.facecolor': 'none', "
"'savefig.facecolor': 'none', "
"'savefig.edgecolor': 'none'"
"}",
]
# Control notebook execution from env for predictable local/CI builds.
# Use values: auto, always, never.
nbsphinx_execute = os.environ.get("UPLT_DOCS_EXECUTE", "auto").strip().lower()
if nbsphinx_execute not in {"auto", "always", "never"}:
nbsphinx_execute = "auto"
# Suppress warnings in nbsphinx kernels without injecting visible cells.
os.environ["PYTHONWARNINGS"] = "ignore"
# Sphinx gallery configuration
sphinx_gallery_conf = {
"doc_module": ("ultraplot",),
"examples_dirs": ["examples"],
"gallery_dirs": ["gallery"],
"filename_pattern": r"^((?!sgskip).)*$",
"min_reported_time": 1,
"plot_gallery": "True",
"reset_modules": ("matplotlib", "seaborn", _reset_ultraplot),
"subsection_order": ExplicitOrder(
[
"examples/layouts",
"examples/legends_colorbars",
"examples/geo",
"examples/plot_types",
"examples/colors",
]
),
"within_subsection_order": FileNameSortKey,
"nested_sections": False,
}
# The name of the Pygments (syntax highlighting) style to use.
# Use non-purple-forward palettes for clearer code contrast in both modes.
pygments_style = "friendly"
pygments_dark_style = "native"
html_baseurl = "https://ultraplot.readthedocs.io/stable"
sitemap_url_scheme = "{link}"
# -- Options for HTML output -------------------------------------------------
# Meta
html_meta = {
"google-site-verification": "jrFbkSQGBUPSYP5LERld7DDSm1UtbMY9O5o3CdzHJzU",
}
# Logo
html_logo = str(Path("_static") / "logo_square.png")
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
# Shibuya is default. Keep legacy RTD-light-dark settings for fallback builds.
if DOCS_THEME == "shibuya":
html_theme = "shibuya"
html_theme_options = {
"toctree_collapse": True,
"toctree_maxdepth": 4,
"toctree_titles_only": True,
"toctree_includehidden": True,
"globaltoc_expand_depth": 1,
"light_logo": "logo_square.png",
"dark_logo": "logo_square.png",
"logo_target": "index.html",
"accent_color": "blue",
"nav_links": [
{"title": "Why UltraPlot?", "url": "why"},
{"title": "Gallery", "url": "gallery/index"},
{"title": "Installation guide", "url": "install"},
{"title": "Usage", "url": "usage"},
{"title": "API", "url": "api"},
{"title": "GitHub", "url": "https://github.com/Ultraplot/UltraPlot"},
{
"title": "Discussions",
"url": "https://github.com/Ultraplot/UltraPlot/discussions",
},
],
}
else:
# Use modified RTD theme with overrides in custom.css and custom.js.
style = None
html_theme = "sphinx_rtd_light_dark"
html_theme_options = {
"logo_only": True,
"collapse_navigation": True,
"navigation_depth": 4,
"prev_next_buttons_location": "bottom", # top and bottom
"includehidden": True,
"titles_only": True,
"sticky_navigation": True,
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
html_sidebars = {
"gallery/index": ["globaltoc.html", "searchbox.html"],
}
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large. Static folder is for CSS and image files. Use ImageMagick to
# convert png to ico on command line with 'convert image.png image.ico'
html_favicon = str(Path("_static") / "logo_blank.svg")
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "ultraplotdoc"
if HAVE_ULTRAPLOT_THEME_EXT:
html_css_files = []
html_js_files = []
else:
html_css_files = ["custom.css"]
html_js_files = ["custom.js"]
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Latex figure (float) alignment
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, "UltraPlot.tex", "UltraPlot Documentation", "UltraPlot", "manual"),
]
primary_domain = "py"
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "UltraPlot", "UltraPlot Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"UltraPlot",
"UltraPlot Documentation",
author,
"UltraPlot",
"A succinct matplotlib wrapper for making beautiful, "
"publication-quality graphics.",
"Miscellaneous",
)
]
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
from ultraplot.internals.docstring import _snippet_manager
def process_docstring(app, what, name, obj, options, lines):
if lines:
try:
# Create a proper format string
doc = "\n".join(lines)
doc = re.sub(r"\\\\\n\\s*", " ", doc)
doc = re.sub(r"\\*\\*kwargs\\b", "``**kwargs``", doc)
doc = re.sub(r"\\*args\\b", "``*args``", doc)
snippet_pattern = re.compile(r"%\\(([^)]+)\\)s")
def _replace_snippet(match):
key = match.group(1)
try:
return str(_snippet_manager[key])
except KeyError:
return match.group(0)
expanded = snippet_pattern.sub(_replace_snippet, doc)
lines[:] = expanded.split("\n")
except Exception as e:
print(f"Warning: Could not expand docstring for {name}: {e}")
# Keep original lines if expansion fails
pass
def setup(app):
app.connect("autodoc-process-docstring", process_docstring)