Skip to content

Commit

Permalink
Fix video embeds, columns, and resource link breakage (#2410)
Browse files Browse the repository at this point in the history
* Centralize resource embed URLs from conf.py

* Run create_resources_listing.py via main() instead of as __main__

* Move git ref and version read earlier in conf.py

* Template in globals via init_globals instead of complicated passing

* Encapsulate runpath.run_py call with default args

* Further cleanup of resource gen

* Remove unused variables

* Stop doing repeated . access on path.suffix

* Use dict for audio and video mimetype info in tags

* Remove cruft from GLSL embed code path (it's not an audio file)

* For n < 3 files, reduce the number of columns

* Add video embeds

* Link GitHub pages for Tiled map .json files

* Move config closer to top

* Add quotes to copyable literals and string-like paths

* Fix brittle relative paths in conf.py

* Set top-level pathlib.Path-based constants

* Add more logging for debug convenience

* Fix spacing + rename default max cols

* Add override for sound listings to avoid overflowing the content pane horizontally

* Asset display tweak

* Quote resource folder captions + fix ":resources:./"
  • Loading branch information
pushfoo authored Oct 16, 2024
1 parent b8e803b commit 8331e50
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 46 deletions.
Binary file modified arcade/resources/assets/images/cybercity_background/foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Modified & re-released under the same license below:
below
Artwork created by Luis Zuno (@ansimuz)
https://ansimuz.itch.io/cyberpunk-street-environment

Expand Down
85 changes: 73 additions & 12 deletions doc/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#!/usr/bin/env python
"""Sphinx configuration file"""
from __future__ import annotations
from functools import cache
import logging
from pathlib import Path
from textwrap import dedent
from typing import Any, NamedTuple
import docutils.nodes
Expand All @@ -20,12 +23,78 @@

# --- Pre-processing Tasks

log = logging.getLogger('conf.py')
logging.basicConfig(level=logging.INFO)

HERE = Path(__file__).resolve()
REPO_LOCAL_ROOT = HERE.parent.parent
ARCADE_MODULE = REPO_LOCAL_ROOT / "arcade"
UTIL_DIR = REPO_LOCAL_ROOT / "util"

log.info(f"Absolute path for our conf.py : {str(HERE)!r}")
log.info(f"Absolute path for the repo root : {str(REPO_LOCAL_ROOT)!r}")
log.info(f"Absolute path for the arcade module : {str(REPO_LOCAL_ROOT)!r}")
log.info(f"Absolute path for the util dir : {str(UTIL_DIR)!r}")

# _temp_version = (REPO_LOCAL_ROOT / "arcade" / "VERSION").read_text().replace("-",'')

sys.path.insert(0, str(REPO_LOCAL_ROOT))
sys.path.insert(0, str(ARCADE_MODULE))
log.info(f"Inserted elements in system path: First two are now:")
for i in range(2):
log.info(f" {i}: {sys.path[i]!r}")

# Don't change to
# from arcade.version import VERSION
# or read the docs build will fail.
from version import VERSION # pyright: ignore [reportMissingImports]
log.info(f"Got version {VERSION!r}")

REPO_URL_BASE="https://github.com/pythonarcade/arcade"
if 'dev' in VERSION:
GIT_REF = 'development'
log.info(f"Got .dev release: using {GIT_REF!r}")
else:
GIT_REF = VERSION
log.info(f"Got real release: using {GIT_REF!r}")


# We'll pass this to our generation scripts to initialize their globals
RESOURCE_GLOBALS = dict(
GIT_REF=GIT_REF,
BASE_URL_REPO=REPO_URL_BASE,
# This double-bracket escapes brackets in f-strings
FMT_URL_REF_PAGE=f"{REPO_URL_BASE}/blob/{GIT_REF}/{{}}",
FMT_URL_REF_EMBED=f"{REPO_URL_BASE}/blob/{GIT_REF}/{{}}?raw=true",
)

def run_util(filename, run_name="__main__", init_globals=None):

full_absolute_path = UTIL_DIR / filename
full_str = str(full_absolute_path)

log.info(f"Running {full_str!r} with:")
log.info(f" run_name={run_name!r}")
kwargs = dict(run_name=run_name)
if init_globals is not None:
kwargs['init_globals'] = init_globals
log.info(f" init_globals={{")
num_left = len(init_globals)
for k, v in init_globals.items():
end = "," if num_left else ""
log.info(f" {k!r} : {v!r}{end}")
num_left -= num_left
log.info(f" }}")

runpy.run_path(full_str, **kwargs)

# Make thumbnails for the example code screenshots
runpy.run_path('../util/generate_example_thumbnails.py', run_name='__main__')
# Create a listing of the resources
runpy.run_path('../util/create_resources_listing.py', run_name='__main__')
run_util("generate_example_thumbnails.py")
# Create a tabular representation of the resources with embeds
run_util("create_resources_listing.py", init_globals=RESOURCE_GLOBALS)
# Run the generate quick API index script
runpy.run_path('../util/update_quick_index.py', run_name='__main__')
run_util('../util/update_quick_index.py')


autodoc_inherit_docstrings = False
autodoc_default_options = {
Expand All @@ -39,16 +108,8 @@
# Special methods in api docs gets a special prefix emoji
prettyspecialmethods_signature_prefix = '🧙'

sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath('../arcade'))

# Don't change to
# from arcade.version import VERSION
# or read the docs build will fail.
from version import VERSION # pyright: ignore [reportMissingImports]

RELEASE = VERSION

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

# Add any Sphinx extension module names here, as strings. They can be
Expand Down
142 changes: 108 additions & 34 deletions util/create_resources_listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
Generate quick API indexes in Restructured Text Format for Sphinx documentation.
"""
import math
import sys
from collections import defaultdict
from functools import lru_cache
from pathlib import Path
from typing import List
import logging

log = logging.getLogger(__name__)

# Ensure we get utility and Arcade imports first
sys.path.insert(0, str(Path(__file__).parent.resolve()))
Expand All @@ -14,13 +20,37 @@
import arcade
from doc_helpers.vfs import Vfs


def announce_templating(var_name):
_v = globals()[var_name]
log.warning(f"Templated {var_name} as {_v!r}")

# The following are provided via runpy.run_path's init_globals keyword
# in conf.py. Uncomment for easy debugger run without IDE config.
try:
_ = GIT_REF # noqa
except Exception as _:
GIT_REF = "development"
announce_templating("GIT_REF")
try:
_URL_BASE = "https://github.com/pythonarcade/arcade"
_ = FMT_URL_REF_PAGE # noqa
except Exception as _:
FMT_URL_REF_PAGE = f"{_URL_BASE}/blob/{GIT_REF}/{{}}"
announce_templating("FMT_URL_REF_PAGE")
try:
_ = FMT_URL_REF_EMBED # noqa
except Exception as _:
FMT_URL_REF_EMBED = f"{_URL_BASE}/blob/{GIT_REF}/{{}}?raw=true"
announce_templating("FMT_URL_REF_EMBED")


MODULE_DIR = Path(__file__).parent.resolve()
ARCADE_ROOT = MODULE_DIR.parent
RESOURCE_DIR = ARCADE_ROOT / "arcade" / "resources"
OUT_FILE = ARCADE_ROOT / "doc" / "api_docs" / "resources.rst"
RESOURCE_URL = "https://github.com/pythonarcade/arcade/blob/development/{}?raw=true"

COLUMNS = 3

# Metadata for the resource list: utils\create_resource_list.py
skip_extensions = [
".glsl",
Expand All @@ -39,6 +69,22 @@ def skipped_file(file_path: Path):
return file_path.suffix in skip_extensions


MAX_COLS: dict[str, int] = defaultdict(lambda: 3)
MAX_COLS[":resources:sounds/"] = 2


@lru_cache(maxsize=None)
def get_header_num_cols(resource_stub: str, n_files = math.inf) -> int:
return int(min(MAX_COLS[resource_stub], n_files))


@lru_cache(maxsize=None)
def get_column_widths_for_n(n: int) -> str:
width = str(100 // n)
return ' '.join((width for _ in range(n)))


@lru_cache(maxsize=None) # Cache b/c re-using elsewhere
def create_resource_path(
path: Path,
prefix: str = "",
Expand Down Expand Up @@ -71,18 +117,25 @@ def process_resource_directory(out, dir: Path):
# out.write("-" * len(cur_node.name) + "\n\n")

file_list = [item for item in path.iterdir() if not (item.is_dir() or skipped_file(item))]
if len(file_list) > 0:
num_files = len(file_list)
if num_files > 0:

# header_title = f":resources:{path.relative_to(RESOURCE_DIR).as_posix()}/"
header_title = create_resource_path(path, suffix="/")
if header_title == ":resources:images/":
raw_header = create_resource_path(path, suffix="/")
header_title = raw_header[:-2] if raw_header.endswith("./") else raw_header

if raw_header == ":resources:images/":
for f in file_list:
print(f.name)
# out.write(f"\n{header_title}\n")
# out.write("-" * (len(header_title)) + "\n\n")

n_cols = get_header_num_cols(raw_header, num_files)
widths = get_column_widths_for_n(n_cols)

out.write(f"\n")
out.write(f".. list-table:: {header_title}\n")
out.write(f" :widths: 33 33 33\n")
out.write(f".. list-table:: \"{header_title}\"\n")
out.write(f" :widths: {widths}\n")
out.write(f" :header-rows: 0\n")
out.write(f" :class: resource-table\n\n")

Expand All @@ -92,46 +145,65 @@ def process_resource_directory(out, dir: Path):
process_resource_directory(out, path)


SUFFIX_TO_AUDIO_TYPE = {
'.wav': 'x-wav',
'.ogg': 'ogg',
'.mp3': 'mpeg',
}
SUFFIX_TO_VIDEO_TYPE = {
'.mp4': 'mp4',
'.webm': 'webm',
'.avi': 'avi'
}

def process_resource_files(out, file_list: List[Path]):
start_row = True
cell_count = 0

prefix = create_resource_path(file_list[0].parent, suffix="/")

COLUMNS = get_header_num_cols(prefix, len(file_list))

log.info(f"Processing {prefix=!r} with {COLUMNS=!r}")
for path in file_list:
resource_path = path.relative_to(ARCADE_ROOT).as_posix()
suffix = path.suffix

if cell_count % COLUMNS == 0:
start_row = "*"
if path.suffix in [".png", ".jpg", ".gif", ".svg"]:
else:
start_row = " "
name = path.name
resource_copyable = f"{create_resource_path(path)}"
if suffix in [".png", ".jpg", ".gif", ".svg"]:
out.write(f" {start_row} - .. image:: ../../{resource_path}\n\n")
out.write(f" {path.name}\n")
cell_count += 1
elif path.suffix == ".wav":
file_path = RESOURCE_URL.format(resource_path)
out.write(f" {name}\n")
elif suffix in SUFFIX_TO_AUDIO_TYPE:
file_path = FMT_URL_REF_EMBED.format(resource_path)
src_type=SUFFIX_TO_AUDIO_TYPE[suffix]
out.write(f" {start_row} - .. raw:: html\n\n")
out.write(f" <audio controls><source src='{file_path}' type='audio/x-wav'></audio><br />{path.name}\n")
cell_count += 1
elif path.suffix == ".mp3":
file_path = RESOURCE_URL.format(resource_path)
out.write(f" <audio controls><source src='{file_path}' type='audio/{src_type}'></audio>\n")
out.write(f" <br /><code class='literal'>&quot;{resource_copyable}&quot;</code>\n")
# out.write(f" <br /><a href={FMT_URL_REF_PAGE.format(resource_path)}>{path.name} on GitHub</a>\n")
elif suffix in SUFFIX_TO_VIDEO_TYPE:
file_path = FMT_URL_REF_EMBED.format(resource_path)
src_type = SUFFIX_TO_VIDEO_TYPE[suffix]
out.write(f" {start_row} - .. raw:: html\n\n")
out.write(f" <audio controls><source src='{file_path}' type='audio/mpeg'></audio><br />{path.name}\n")
cell_count += 1
elif path.suffix == ".ogg":
file_path = RESOURCE_URL.format(resource_path)
out.write(f" {start_row} - .. raw:: html\n\n")
out.write(f" <audio controls><source src='{file_path}' type='audio/ogg'></audio><br />{path.name}\n")
cell_count += 1
elif path.suffix == ".glsl":
file_path = RESOURCE_URL.format(resource_path)
out.write(f" {start_row} - `{path.name} <{file_path}>`_\n")
# out.write(f" {start_row} - .. raw:: html\n\n")
# out.write(f" <audio controls><source src='{file_path}' type='audio/ogg'></audio><br />{path.name}\n")
cell_count += 1
out.write(f" <video style=\"max-width: 100%\" controls><source src='{file_path}' type='video/{src_type}'></video>\n")
out.write(f" <br /><code class='literal'>&quot;{resource_copyable}&quot;</code>\n")
elif suffix == ".glsl":
file_path = FMT_URL_REF_PAGE.format(resource_path)
out.write(f" {start_row} - `{path} <{file_path}>`_\n")
# Link Tiled maps
elif suffix == ".json":
file_path = FMT_URL_REF_PAGE.format(resource_path)
out.write(f" {start_row} - `{name} <{file_path}>`_\n")
else:
out.write(f" {start_row} - {path.name}\n")
cell_count += 1

start_row = " "
out.write(f" {start_row} - {name}\n")
# The below doesn't work because of how raw HTML / Sphinx images interact:
# out.write(f" <br /><code class='literal'>{resource_copyable}</code>\n")
cell_count += 1

# Finish any remaining columns with empty cells
while cell_count % COLUMNS > 0:
out.write(f" -\n")
cell_count += 1
Expand Down Expand Up @@ -161,8 +233,10 @@ def resources():
out.close()
print("Done creating resources.rst")


vfs = Vfs()


def main():
resources()
vfs.write()
Expand Down

0 comments on commit 8331e50

Please sign in to comment.