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()