Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
SPHINXOPTS =
SPHINXBUILD = sphinx-build
BUILDDIR = _build
FFMPEGDIR = _ffmpeg
PYAV_PIP ?= pip
PIP := $(PYAV_PIP)
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
Expand All @@ -10,14 +9,6 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .

default: html

TAGFILE := _build/doxygen/tagfile.xml

$(TAGFILE):
@if [ ! -d "$(FFMPEGDIR)" ]; then \
git clone --depth=1 git://source.ffmpeg.org/ffmpeg.git $(FFMPEGDIR); \
fi
./generate-tagfile --library $(FFMPEGDIR) -o $(TAGFILE)

TEMPLATES := $(wildcard api/*.py development/*.py)
RENDERED := $(TEMPLATES:%.py=_build/rst/%.rst)

Expand All @@ -30,7 +21,7 @@ clean:
rm -rf $(BUILDDIR) $(FFMPEGDIR)

html: $(RENDERED) $(TAGFILE)
$(PIP) install -U sphinx==5.1.0
$(PIP) install -U sphinx
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html

test:
Expand Down
210 changes: 27 additions & 183 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
import logging
import os
import re
import sys
import xml.etree.ElementTree as etree

import sphinx
from docutils import nodes
from sphinx import addnodes
from sphinx.util.docutils import SphinxDirective
import sphinx


logging.basicConfig()

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(".."))

# -- General configuration -----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
Expand All @@ -31,37 +18,24 @@
"sphinx.ext.doctest",
]


# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix of source filenames.
source_suffix = ".rst"

# The master toctree document.
master_doc = "index"

# General information about the project.
project = "PyAV"
copyright = "2025, The PyAV Team"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#

about = {}
with open("../av/about.py") as fp:
exec(fp.read(), about)

# The full version, including alpha/beta/rc tags.
release = about["__version__"]

# The short X.Y version.
version = release.split("-")[0]

exclude_patterns = ["_build"]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# -- Options for HTML output ---------------------------------------------------
Expand All @@ -85,7 +59,6 @@


doctest_global_setup = """

import errno
import os

Expand All @@ -109,29 +82,20 @@ def sandboxed(*args, **kwargs):
os.chdir(here)

video_path = curated('pexels/time-lapse-video-of-night-sky-857195.mp4')

"""

doctest_global_cleanup = """

os.chdir(_cwd)

"""


doctest_global_cleanup = "os.chdir(_cwd)"
doctest_test_doctest_blocks = ""


extlinks = {
"ffstruct": ("http://ffmpeg.org/doxygen/trunk/struct%s.html", "struct "),
"issue": ("https://github.com/PyAV-Org/PyAV/issues/%s", "#"),
"pr": ("https://github.com/PyAV-Org/PyAV/pull/%s", "#"),
"gh-user": ("https://github.com/%s", "@"),
"ffmpeg": ("https://ffmpeg.org/doxygen/trunk/%s.html", "%s"),
"ffstruct": ("https://ffmpeg.org/doxygen/trunk/struct%s.html", "struct %s"),
"issue": ("https://github.com/PyAV-Org/PyAV/issues/%s", "#%s"),
"pr": ("https://github.com/PyAV-Org/PyAV/pull/%s", "#%s"),
"gh-user": ("https://github.com/%s", "@%s"),
}

intersphinx_mapping = {
"https://docs.python.org/3": None,
}
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}

autodoc_member_order = "bysource"
autodoc_default_options = {
Expand Down Expand Up @@ -238,152 +202,32 @@ def makerow(*texts):
)

seen = set()
if hasattr(enum, "_by_name"): # Our custom enum class
enum_items = enum._by_name.items()
for name, item in enum_items:
if name.lower() in seen:
continue
seen.add(name.lower())

try:
attr = properties[item]
except KeyError:
if cls:
continue
attr = None

value = f"0x{item.value:X}"
doc = item.__doc__ or "-"
tbody += makerow(attr, name, value, doc)

return [table]
else: # Standard IntEnum
enum_items = [
(name, item)
for name, item in vars(enum).items()
if isinstance(item, enum)
]
for name, item in enum_items:
if name.lower() in seen:
continue
seen.add(name.lower())

try:
attr = properties[item]
except KeyError:
if cls:
continue
attr = None

value = f"0x{item.value:X}"
doc = enum.__annotations__.get(name, "---")[1:-1]
tbody += makerow(attr, name, value, doc)

return [table]


doxylink = {}
ffmpeg_tagfile = os.path.abspath(
os.path.join(__file__, "..", "_build", "doxygen", "tagfile.xml")
)
if not os.path.exists(ffmpeg_tagfile):
print("ERROR: Missing FFmpeg tagfile.")
exit(1)
doxylink["ffmpeg"] = (ffmpeg_tagfile, "https://ffmpeg.org/doxygen/trunk/")


def doxylink_create_handler(app, file_name, url_base):
print("Finding all names in Doxygen tagfile", file_name)

doc = etree.parse(file_name)
root = doc.getroot()

parent_map = {} # ElementTree doesn't five us access to parents.
urls = {}

for node in root.findall(".//name/.."):
for child in node:
parent_map[child] = node

kind = node.attrib["kind"]
if kind not in ("function", "struct", "variable"):
continue

name = node.find("name").text

if kind not in ("function",):
parent = parent_map.get(node)
parent_name = parent.find("name") if parent else None
if parent_name is not None:
name = f"{parent_name.text}.{name}"

filenode = node.find("filename")
if filenode is not None:
url = filenode.text
else:
url = "{}#{}".format(
node.find("anchorfile").text,
node.find("anchor").text,
)

urls.setdefault(kind, {})[name] = url

def get_url(name):
# These are all the kinds that seem to exist.
for kind in (
"function",
"struct",
"variable", # These are struct members.
# 'class',
# 'define',
# 'enumeration',
# 'enumvalue',
# 'file',
# 'group',
# 'page',
# 'typedef',
# 'union',
):
enum_items = [
(name, item)
for name, item in vars(enum).items()
if isinstance(item, enum)
]
for name, item in enum_items:
if name.lower() in seen:
continue
seen.add(name.lower())

try:
return urls[kind][name]
attr = properties[item]
except KeyError:
pass

def _doxylink_handler(name, rawtext, text, lineno, inliner, options={}, content=[]):
m = re.match(r"^(.+?)(?:<(.+?)>)?$", text)
title, name = m.groups()
name = name or title

url = get_url(name)
if not url:
if name == "AVFrame.color_primaries":
url = "structAVFrame.html#a59a3f830494f2ed1133103a1bc9481e7"
elif name == "AVFrame.color_trc":
url = "structAVFrame.html#ab09abb126e3922bc1d010cf044087939"
else:
print("ERROR: Could not find", name)
exit(1)

node = addnodes.literal_strong(title, title)
if url:
url = url_base + url
node = nodes.reference("", "", node, refuri=url)
if cls:
continue
attr = None

return [node], []
value = f"0x{item.value:X}"
doc = enum.__annotations__.get(name, "---")[1:-1]
tbody += makerow(attr, name, value, doc)

return _doxylink_handler
return [table]


def setup(app):
app.add_css_file("custom.css")

app.add_directive("flagtable", EnumTable)
app.add_directive("enumtable", EnumTable)
app.add_directive("pyinclude", PyInclude)

skip = os.environ.get("PYAV_SKIP_DOXYLINK")
for role, (filename, url_base) in doxylink.items():
if skip:
app.add_role(role, lambda *args: ([], []))
else:
app.add_role(role, doxylink_create_handler(app, filename, url_base))
Loading
Loading