From 8331e502101d23aec646028ddc7d48c174f49561 Mon Sep 17 00:00:00 2001 From: Paul <36696816+pushfoo@users.noreply.github.com> Date: Wed, 16 Oct 2024 03:14:01 -0400 Subject: [PATCH] Fix video embeds, columns, and resource link breakage (#2410) * 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:./" --- .../cybercity_background/foreground.png | Bin 15730 -> 15231 bytes .../cybercity_background/public-license.txt | 2 + doc/conf.py | 85 +++++++++-- util/create_resources_listing.py | 142 +++++++++++++----- 4 files changed, 183 insertions(+), 46 deletions(-) diff --git a/arcade/resources/assets/images/cybercity_background/foreground.png b/arcade/resources/assets/images/cybercity_background/foreground.png index bcf427ba191e723355695d98ed4f2ccc3d455437..ae5b2f9f9795816474b77a36bf9b35e217e10b9c 100644 GIT binary patch literal 15231 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 literal 15730 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 Kbd98G$o~P$Zda!O diff --git a/arcade/resources/assets/images/cybercity_background/public-license.txt b/arcade/resources/assets/images/cybercity_background/public-license.txt index 0029ea46b..5ef285294 100644 --- a/arcade/resources/assets/images/cybercity_background/public-license.txt +++ b/arcade/resources/assets/images/cybercity_background/public-license.txt @@ -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 diff --git a/doc/conf.py b/doc/conf.py index bea226a48..a1d00dce1 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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 @@ -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 = { @@ -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 diff --git a/util/create_resources_listing.py b/util/create_resources_listing.py index 274aecd80..2562560f7 100644 --- a/util/create_resources_listing.py +++ b/util/create_resources_listing.py @@ -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())) @@ -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", @@ -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 = "", @@ -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") @@ -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"
{path.name}\n") - cell_count += 1 - elif path.suffix == ".mp3": - file_path = RESOURCE_URL.format(resource_path) + out.write(f" \n") + out.write(f"
"{resource_copyable}"\n") + # out.write(f"
{path.name} on GitHub\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"
{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"
{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"
{path.name}\n") - cell_count += 1 + out.write(f" \n") + out.write(f"
"{resource_copyable}"\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"
{resource_copyable}\n") + cell_count += 1 + # Finish any remaining columns with empty cells while cell_count % COLUMNS > 0: out.write(f" -\n") cell_count += 1 @@ -161,8 +233,10 @@ def resources(): out.close() print("Done creating resources.rst") + vfs = Vfs() + def main(): resources() vfs.write()