1# Configuration file for the Sphinx documentation builder. 2# 3# For the full list of built-in configuration values, see the documentation: 4# https://www.sphinx-doc.org/en/master/usage/configuration.html 5 6# -- Path setup -------------------------------------------------------------- 7 8# If extensions (or modules to document with autodoc) are in another directory, 9# add these directories to sys.path here. If the directory is relative to the 10# documentation root, use os.path.abspath to make it absolute, like shown here. 11 12import os 13import shutil 14import sys 15import typing 16import datetime 17import importlib 18import sphobjinv 19import functools 20import pylit 21from sphinx.ext.napoleon.docstring import NumpyDocstring 22 23sys.path.insert(0, os.path.abspath('.')) 24_today = datetime.datetime.now() 25 26 27# -- Project information ----------------------------------------------------- 28# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 29 30package = 'petsc4py' 31 32 33def pkg_version(): 34 import re 35 here = os.path.dirname(__file__) 36 pardir = [os.path.pardir] * 2 37 topdir = os.path.join(here, *pardir) 38 srcdir = os.path.join(topdir, 'src') 39 with open(os.path.join(srcdir, 'petsc4py', '__init__.py')) as f: 40 m = re.search(r"__version__\s*=\s*'(.*)'", f.read()) 41 return m.groups()[0] 42 43 44project = 'PETSc for Python' 45author = 'Lisandro Dalcin' 46copyright = f'{_today.year}, {author}' 47 48release = pkg_version() 49version = release.rsplit('.', 1)[0] 50 51 52# -- General configuration --------------------------------------------------- 53# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 54 55extensions = [ 56 'sphinx.ext.autodoc', 57 'sphinx.ext.autosummary', 58 'sphinx.ext.intersphinx', 59 'sphinx.ext.napoleon', 60 'sphinx.ext.extlinks', 61] 62 63templates_path = ['_templates'] 64exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 65 66needs_sphinx = '5.0.0' 67 68default_role = 'any' 69 70pygments_style = 'tango' 71 72nitpicky = True 73nitpick_ignore = [ 74 ('envvar', 'NUMPY_INCLUDE'), 75 ('py:class', 'ndarray'), # FIXME 76 ('py:class', 'typing_extensions.Self'), 77] 78nitpick_ignore_regex = [ 79 (r'c:.*', r'MPI_.*'), 80 (r'c:.*', r'Petsc.*'), 81 (r'envvar', r'(LD_LIBRARY_)?PATH'), 82 (r'envvar', r'(MPICH|OMPI|MPIEXEC)_.*'), 83] 84 85toc_object_entries = False 86toc_object_entries_show_parents = 'hide' 87# python_use_unqualified_type_names = True 88 89autodoc_class_signature = 'separated' 90autodoc_typehints = 'description' 91autodoc_typehints_format = 'short' 92autodoc_mock_imports = [] 93autodoc_type_aliases = {} 94 95autosummary_context = { 96 'synopsis': {}, 97 'autotype': {}, 98} 99 100# This can be customized 101www = 'https://gitlab.com/petsc/petsc/-/tree/main' 102extlinks = {'sources': (f'{www}/src/binding/petsc4py/src/%s','')} 103 104def _mangle_petsc_intersphinx(): 105 """Preprocess the keys in PETSc's intersphinx inventory. 106 107 PETSc have intersphinx keys of the form: 108 109 manualpages/Vec/VecShift 110 111 instead of: 112 113 petsc.VecShift 114 115 This function downloads their object inventory and strips the leading path 116 elements so that references to PETSc names actually resolve.""" 117 inv = sphobjinv.Inventory(url="https://petsc.org/main/objects.inv") 118 119 for obj in inv.objects: 120 if obj.name.startswith("manualpages"): 121 obj.name = "petsc." + "/".join(obj.name.split("/")[2:]) 122 obj.role = "class" 123 obj.domain = "py" 124 125 sphobjinv.writebytes("petsc_objects.inv", 126 sphobjinv.compress(inv.data_file(contract=True))) 127 128 129_mangle_petsc_intersphinx() 130 131 132intersphinx_mapping = { 133 'python': ('https://docs.python.org/3/', None), 134 'numpy': ('https://numpy.org/doc/stable/', None), 135 'numpydoc': ('https://numpydoc.readthedocs.io/en/latest/', None), 136 'mpi4py': ('https://mpi4py.readthedocs.io/en/stable/', None), 137 'pyopencl': ('https://documen.tician.de/pyopencl/', None), 138 'dlpack': ('https://dmlc.github.io/dlpack/latest/', None), 139 'petsc': ('https://petsc.org/main/', 'petsc_objects.inv'), 140} 141 142napoleon_preprocess_types = True 143 144try: 145 import sphinx_rtd_theme 146 if 'sphinx_rtd_theme' not in extensions: 147 extensions.append('sphinx_rtd_theme') 148except ImportError: 149 sphinx_rtd_theme = None 150 151 152def _setup_mpi4py_typing(): 153 pkg = type(sys)('mpi4py') 154 mod = type(sys)('mpi4py.MPI') 155 mod.__package__ = pkg.__name__ 156 sys.modules[pkg.__name__] = pkg 157 sys.modules[mod.__name__] = mod 158 for clsname in ( 159 'Intracomm', 160 'Datatype', 161 'Op', 162 ): 163 cls = type(clsname, (), {}) 164 cls.__module__ = mod.__name__ 165 setattr(mod, clsname, cls) 166 167 168def _patch_domain_python(): 169 from sphinx.domains.python import PythonDomain 170 PythonDomain.object_types['data'].roles += ('class',) 171 172 173def _setup_autodoc(app): 174 from sphinx.ext import autodoc 175 from sphinx.util import inspect 176 from sphinx.util import typing 177 178 # 179 180 def stringify_annotation(annotation, mode='fully-qualified-except-typing'): 181 qualname = getattr(annotation, '__qualname__', '') 182 module = getattr(annotation, '__module__', '') 183 args = getattr(annotation, '__args__', None) 184 if module == 'builtins' and qualname and args is not None: 185 args = ', '.join(stringify_annotation(a, mode) for a in args) 186 return f'{qualname}[{args}]' 187 return stringify_annotation_orig(annotation, mode) 188 189 try: 190 stringify_annotation_orig = typing.stringify_annotation 191 inspect.stringify_annotation = stringify_annotation 192 typing.stringify_annotation = stringify_annotation 193 autodoc.stringify_annotation = stringify_annotation 194 autodoc.typehints.stringify_annotation = stringify_annotation 195 except AttributeError: 196 stringify_annotation_orig = typing.stringify 197 inspect.stringify_annotation = stringify_annotation 198 typing.stringify = stringify_annotation 199 autodoc.stringify_typehint = stringify_annotation 200 201 # 202 203 class ClassDocumenterMixin: 204 205 def __init__(self, *args, **kwargs): 206 super().__init__(*args, **kwargs) 207 if self.config.autodoc_class_signature == 'separated': 208 members = self.options.members 209 special_members = self.options.special_members 210 if special_members is not None: 211 for name in ('__new__', '__init__'): 212 if name in members: 213 members.remove(name) 214 if name in special_members: 215 special_members.remove(name) 216 217 class ClassDocumenter( 218 ClassDocumenterMixin, 219 autodoc.ClassDocumenter, 220 ): 221 pass 222 223 class ExceptionDocumenter( 224 ClassDocumenterMixin, 225 autodoc.ExceptionDocumenter, 226 ): 227 pass 228 229 app.add_autodocumenter(ClassDocumenter, override=True) 230 app.add_autodocumenter(ExceptionDocumenter, override=True) 231 232 233def _monkey_patch_returns(): 234 """Rewrite the role of names in "Returns" sections. 235 236 This is needed because Napoleon uses ``:class:`` for the return types 237 and this does not work with type aliases like ``ArrayScalar``. To resolve 238 this we swap ``:class:`` for ``:any:``. 239 240 """ 241 _parse_returns_section = \ 242 NumpyDocstring._parse_returns_section 243 244 @functools.wraps(NumpyDocstring._parse_returns_section) 245 def wrapper(*args, **kwargs): 246 out = _parse_returns_section(*args, **kwargs) 247 return [line.replace(":class:", ":any:") for line in out] 248 249 NumpyDocstring._parse_returns_section = wrapper 250 251 252def _monkey_patch_see_also(): 253 """Rewrite the role of names in "see also" sections. 254 255 Napoleon uses :obj: for all names found in "see also" sections but we 256 need :all: so that references to labels work.""" 257 258 _parse_numpydoc_see_also_section = \ 259 NumpyDocstring._parse_numpydoc_see_also_section 260 261 @functools.wraps(NumpyDocstring._parse_numpydoc_see_also_section) 262 def wrapper(*args, **kwargs): 263 out = _parse_numpydoc_see_also_section(*args, **kwargs) 264 return [line.replace(":obj:", ":any:") for line in out] 265 266 NumpyDocstring._parse_numpydoc_see_also_section = wrapper 267 268 269def _apply_monkey_patches(): 270 """Modify Napoleon types after parsing to make references work.""" 271 _monkey_patch_returns() 272 _monkey_patch_see_also() 273 274 275_apply_monkey_patches() 276 277 278def _process_demos(*demos): 279 # Convert demo .py files to rst. Also copy the .py file so it can be 280 # linked from the demo rst file. 281 try: 282 os.mkdir("demo") 283 except FileExistsError: 284 pass 285 for demo in demos: 286 demo_dir = os.path.join("demo", os.path.dirname(demo)) 287 demo_src = os.path.join(os.pardir, os.pardir, "demo", demo) 288 try: 289 os.mkdir(demo_dir) 290 except FileExistsError: 291 pass 292 with open(demo_src, "r") as infile: 293 with open(os.path.join( 294 os.path.join("demo", os.path.splitext(demo)[0] + ".rst")), "w" 295 ) as outfile: 296 converter = pylit.Code2Text(infile) 297 outfile.write(str(converter)) 298 demo_copy_name = os.path.join(demo_dir, os.path.basename(demo)) 299 shutil.copyfile(demo_src, demo_copy_name) 300 html_static_path.append(demo_copy_name) 301 with open(os.path.join("demo", "demo.rst"), "w") as demofile: 302 demofile.write(""" 303petsc4py demos 304============== 305 306.. toctree:: 307 308""") 309 for demo in demos: 310 demofile.write(" " + os.path.splitext(demo)[0] + "\n") 311 demofile.write("\n") 312 313html_static_path=[] 314_process_demos( 315 "poisson2d/poisson2d.py" 316) 317 318 319def setup(app): 320 _setup_mpi4py_typing() 321 _patch_domain_python() 322 _monkey_patch_returns() 323 _monkey_patch_see_also() 324 _setup_autodoc(app) 325 326 try: 327 from petsc4py import PETSc 328 except ImportError: 329 autodoc_mock_imports.append('PETSc') 330 return 331 del PETSc.DA # FIXME 332 333 sys_dwb = sys.dont_write_bytecode 334 sys.dont_write_bytecode = True 335 import apidoc 336 sys.dont_write_bytecode = sys_dwb 337 338 name = PETSc.__name__ 339 here = os.path.abspath(os.path.dirname(__file__)) 340 outdir = os.path.join(here, apidoc.OUTDIR) 341 source = os.path.join(outdir, f'{name}.py') 342 getmtime = os.path.getmtime 343 generate = ( 344 not os.path.exists(source) 345 or getmtime(source) < getmtime(PETSc.__file__) 346 or getmtime(source) < getmtime(apidoc.__file__) 347 ) 348 if generate: 349 apidoc.generate(source) 350 module = apidoc.load_module(source) 351 apidoc.replace_module(module) 352 353 modules = [ 354 'petsc4py', 355 ] 356 typing_overload = typing.overload 357 typing.overload = lambda arg: arg 358 for name in modules: 359 mod = importlib.import_module(name) 360 ann = apidoc.load_module(f'{mod.__file__}i', name) 361 apidoc.annotate(mod, ann) 362 typing.overload = typing_overload 363 364 from petsc4py import typing as tp 365 for attr in tp.__all__: 366 autodoc_type_aliases[attr] = f'~petsc4py.typing.{attr}' 367 368# -- Options for HTML output ------------------------------------------------- 369# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 370 371# The theme to use for HTML and HTML Help pages. See the documentation for 372# a list of builtin themes. 373html_theme = 'pydata_sphinx_theme' 374 375# -- Options for HTMLHelp output ------------------------------------------ 376 377# Output file base name for HTML help builder. 378htmlhelp_basename = f'{package}-man' 379 380 381# -- Options for LaTeX output --------------------------------------------- 382 383# (source start file, target name, title, 384# author, documentclass [howto, manual, or own class]). 385latex_documents = [ 386 ('index', f'{package}.tex', project, author, 'howto'), 387] 388 389latex_elements = { 390 'papersize': 'a4', 391} 392 393 394# -- Options for manual page output --------------------------------------- 395 396# (source start file, name, description, authors, manual section). 397man_pages = [ 398 ('index', package, project, [author], 3) 399] 400 401 402# -- Options for Texinfo output ------------------------------------------- 403 404# (source start file, target name, title, author, 405# dir menu entry, description, category) 406texinfo_documents = [ 407 ('index', package, project, author, 408 package, f'{project}.', 'Miscellaneous'), 409] 410 411 412# -- Options for Epub output ---------------------------------------------- 413 414# Output file base name for ePub builder. 415epub_basename = package 416