diff --git a/arcade/resources/assets/images/cybercity_background/foreground.png b/arcade/resources/assets/images/cybercity_background/foreground.png index bcf427ba1..ae5b2f9f9 100644 Binary files a/arcade/resources/assets/images/cybercity_background/foreground.png and b/arcade/resources/assets/images/cybercity_background/foreground.png differ 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()