From 818ac9559b14b61f2e85262bd1bc5b48116032b1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 15 Jul 2026 03:52:22 +0000 Subject: [PATCH] Add Google-style docstrings for public API reference Document module overviews, WIN16, get_palette, Palette, MappedPalette, WheelPalette, CubePalette, and MDPalette with Args, Returns, Raises, and Examples so mkdocstrings generates complete RTD API pages. Co-authored-by: Brad Barnett --- src/palettes/__init__.py | 148 +++++++++++++++++++++++++++++--- src/palettes/cube.py | 44 +++++----- src/palettes/material_design.py | 59 ++++++------- src/palettes/wheel.py | 60 +++++++------ 4 files changed, 219 insertions(+), 92 deletions(-) diff --git a/src/palettes/__init__.py b/src/palettes/__init__.py index 8f73463..1e0e501 100644 --- a/src/palettes/__init__.py +++ b/src/palettes/__init__.py @@ -1,12 +1,24 @@ # SPDX-FileCopyrightText: 2024 Brad Barnett # # SPDX-License-Identifier: MIT -""" -`palettes` -==================================================== +"""Color palette toolkit for pydisplay. + +Provides named color sets, RGB/HSV wheels, RGB cubes, and Material Design +swatches. Palettes map integer indices to display-ready color values at +several bit depths (4, 8, 16, or 24). + +Example: + >>> from palettes import get_palette + >>> palette = get_palette(name="wheel", length=256, saturation=1.0) + >>> palette[0] + >>> palette.color_name(0) + +Attributes: + WIN16: Mapping of ``0xRRGGBB`` values to Windows 16-color names. Used as + the default name table for :class:`Palette` and + :class:`~palettes.wheel.WheelPalette`. """ -# The 16 colors of the standard Windows 16-color palette WIN16 = { 0x000000: "Black", 0x000080: "Navy", @@ -28,6 +40,21 @@ def get_palette(name="default", **kwargs): + """Construct a palette by logical name. + + Args: + name: Palette type. One of ``"default"`` (Windows 16-color), + ``"wheel"``, ``"cube"``, or ``"material_design"``. Unknown names + fall back to :class:`Palette`. + **kwargs: Forwarded to the palette constructor (for example + ``color_depth``, ``length``, ``size``, ``saturation``). + + Returns: + A :class:`Palette` subclass instance. + + Example: + >>> get_palette(name="cube", size=3, color_depth=16) + """ if name == "wheel": from .wheel import WheelPalette as MyPalette elif name == "material_design": @@ -40,8 +67,21 @@ def get_palette(name="default", **kwargs): class Palette: - """ - A class to represent a color palette. + """Indexed color palette with optional named color attributes. + + Subclasses override :meth:`_get_rgb` to define how each index maps to + red, green, and blue components. :meth:`__getitem__` converts those + components to the configured ``color_depth``. + + Named colors from the palette's name table (for example ``palette.RED``) + are attached as attributes during initialization. + + Args: + name: Optional label stored in :attr:`name`. + color_depth: Output format for :meth:`__getitem__`: ``4`` (24-bit + index), ``8`` (RGB332), ``16`` (RGB565), or ``24`` (``0xRRGGBB``). + swapped: If ``True``, byte-swap 16-bit colors (little-endian displays). + cached: If ``True``, memoize computed index colors in an internal dict. """ def __init__(self, name="", color_depth=16, swapped=False, cached=False): @@ -69,16 +109,32 @@ def _define_named_colors(self): @property def name(self): + """Human-readable palette label.""" return self._name def __iter__(self): + """Yield each palette entry in index order.""" for i in range(len(self)): yield self[i] def __len__(self): + """Number of colors in the palette.""" return self._length def __getitem__(self, index): + """Return the color at ``index`` in the configured bit depth. + + Negative indices and indices beyond the palette length wrap around. + + Args: + index: Color index (supports negative and out-of-range values). + + Returns: + Color value as an integer (format depends on ``color_depth``). + + Raises: + ValueError: If ``color_depth`` is not 4, 8, 16, or 24. + """ index = self._normalize(index) if self._cache is not None and index in self._cache: @@ -101,6 +157,17 @@ def _normalize(self, index): return index def color565(self, r, g=None, b=None): + """Convert RGB to a 16-bit RGB565 value. + + Args: + r: Red component (0–255), a 24-bit ``0xRRGGBB`` integer, or an + ``(r, g, b)`` sequence. + g: Green component when ``r`` is passed separately. + b: Blue component when ``r`` is passed separately. + + Returns: + 16-bit color, optionally byte-swapped when ``swapped`` is ``True``. + """ if isinstance(r, (tuple, list)): # r is a tuple or list r, g, b = r @@ -115,6 +182,17 @@ def color565(self, r, g=None, b=None): return color def color332(self, r, g=None, b=None): + """Convert RGB to an 8-bit RGB332 value. + + Args: + r: Red component (0–255), a 24-bit ``0xRRGGBB`` integer, or an + ``(r, g, b)`` sequence. + g: Green component when ``r`` is passed separately. + b: Blue component when ``r`` is passed separately. + + Returns: + 8-bit RGB332 color. + """ # Convert r, g, b to 8-bit if isinstance(r, (tuple, list)): # r is a tuple or list @@ -127,8 +205,14 @@ def color332(self, r, g=None, b=None): return color def color_rgb(self, color): - """ - color can be an 16-bit integer or a tuple, list or bytearray of length 2 or 3. + """Expand a packed color to an ``(r, g, b)`` tuple. + + Args: + color: A 16-bit integer, or a 2- or 3-byte sequence in display + byte order. + + Returns: + ``(red, green, blue)`` with each component in ``0``–``255``. """ if isinstance(color, int): # convert 16-bit int color to 2 bytes @@ -142,18 +226,53 @@ def color_rgb(self, color): return (r, g, b) def color_name(self, index): + """Return the name of the color at ``index``. + + Args: + index: Palette index (supports wrapping). + + Returns: + A name from the palette name table, or a ``"#RRGGBB"`` hex string + when no name matches. + """ return self.rgb_name(self._get_rgb(self._normalize(index))) def rgb_name(self, r, g=None, b=None): + """Look up a color name from RGB components. + + Args: + r: Red (0–255), a 24-bit integer, or an ``(r, g, b)`` sequence. + g: Green when ``r`` is passed separately. + b: Blue when ``r`` is passed separately. + + Returns: + Matching name from :attr:`_names`, or ``"#RRGGBB"`` if unknown. + """ if isinstance(r, (tuple, list)): r, g, b = r return self._names.get(r << 16 | g << 8 | b, f"#{r:02X}{g:02X}{b:02X}") def luminance(self, index): + """Perceived brightness of the color at ``index`` (ITU-R BT.601). + + Args: + index: Palette index. + + Returns: + Luminance in ``0.0``–``255.0``. + """ r, g, b = self._get_rgb(index) return 0.299 * r + 0.587 * g + 0.114 * b def brightness(self, index): + """Average channel brightness of the color at ``index``. + + Args: + index: Palette index. + + Returns: + Normalized brightness in ``0.0``–``1.0``. + """ r, g, b = self._get_rgb(index) return (r + g + b) / 3 / 255 @@ -163,8 +282,17 @@ def _get_rgb(self, index): class MappedPalette(Palette): - """ - A class to represent a color palette with a color map. + """Palette backed by a flat RGB byte map. + + Each color occupies three consecutive bytes ``(r, g, b)`` in + ``color_map``. Subclasses such as :class:`~palettes.material_design.MDPalette` + supply a pre-built map and named-color attributes. + + Args: + name: Optional label stored in :attr:`name`. + color_depth: Output format for :meth:`Palette.__getitem__`. + swapped: Byte-swap 16-bit colors when ``True``. + color_map: ``bytes`` or buffer of RGB triplets, length ``3 * n_colors``. """ def __init__(self, name, color_depth, swapped, color_map): diff --git a/src/palettes/cube.py b/src/palettes/cube.py index c463b63..139b849 100644 --- a/src/palettes/cube.py +++ b/src/palettes/cube.py @@ -1,32 +1,34 @@ -# SPDIX:# SPDX-FileCopyrightText: 2024 Brad Barnett +# SPDX-FileCopyrightText: 2024 Brad Barnett # # SPDX-License-Identifier: MIT -""" -`palettes.cube` -==================================================== -Makes a color cube palette. - -Usage: - from palettes import get_palette - palette = get_palette(name="cube", size=5, color_depth=16, swapped=False) - # OR - palette = get_palette(name="cube") - - # OR - from palettes.cube import CubePalette - palette = CubePalette(size=5, color_depth=24) - - print(f"Palette: {palette.name}, Length: {len(palette)}") - for i, color in enumerate(palette): - for i, color in enumerate(palette): print(f"{i}. {color:#06X} {palette.color_name(i)}") +"""RGB color-cube palettes. + +Samples the RGB cube at evenly spaced points. Supported cube sizes are +2, 3, 4, and 5 (8, 27, 64, or 125 colors). Each size uses a built-in +name table for :meth:`~palettes.Palette.color_name`. + +Example: + >>> from palettes import get_palette + >>> palette = get_palette(name="cube", size=5, color_depth=16) + >>> len(palette) + 125 """ from . import Palette as _Palette class CubePalette(_Palette): - """ - A color cube palette. The size of the cube is determined by the size parameter. + """Evenly spaced RGB cube palette. + + Indices traverse the cube in ``x``, then ``y``, then ``z`` order. Channel + values are spaced from ``0`` to ``255`` inclusive. + + Args: + name: Prefix for :attr:`~palettes.Palette.name` (length suffix is added). + color_depth: Output format; see :class:`~palettes.Palette`. + swapped: Byte-swap 16-bit colors when ``True``. + cached: Memoize index lookups when ``True`` (default). + size: Cube edge length. Must be ``2``, ``3``, ``4``, or ``5``. """ def __init__(self, name="", color_depth=16, swapped=False, cached=True, size=5): diff --git a/src/palettes/material_design.py b/src/palettes/material_design.py index 314922d..6ee10a3 100644 --- a/src/palettes/material_design.py +++ b/src/palettes/material_design.py @@ -1,41 +1,19 @@ # SPDX-FileCopyrightText: 2024 Brad Barnett # # SPDX-License-Identifier: MIT -""" -`palettes.material_design` -==================================================== -This module contains the Material Design color palette as a class object. - - -Usage: - from palettes import get_palette - palette = get_palette(name="material_design", color_depth=16, swapped=False) - # OR - palette = get_palette("material_design") - - # OR - from palettes.material_design import MDPalette - palette = MDPalette(size=5, color_depth=24) - - # to access the primary variant of a color family by name: - x = palette.RED - x = palette.BLACK +"""Material Design color palette. - # to access all 256 colors directly: - x = palette[127] # color at index 127 +256 swatches from the Material Design spec, exposed as indexed colors and +named attributes. Each hue family includes shades ``S50``–``S900``; most +families also provide accent colors ``A100``, ``A200``, ``A400``, and +``A700``. - # to access a shade by name: - x = palette.RED_S500 # shade 500 - x = palette.RED_S900 # shade 900 - x = palette.RED_S50 # shade 50 - - # to access an accent of a color family by name: - x = palette.RED_A100 - x = palette.RED_A700 - - # to iterate over all 256 colors: - for x in palette: - pass +Example: + >>> from palettes import get_palette + >>> palette = get_palette(name="material_design", color_depth=16) + >>> palette.RED + >>> palette.RED_S900 + >>> palette[127] """ from . import MappedPalette @@ -43,8 +21,19 @@ class MDPalette(MappedPalette): - """ - A class to represent the Material Design color palette. + """Material Design swatch palette. + + Colors are stored in a flat RGB map. During initialization, named + attributes are created for each family and shade (for example + ``RED``, ``RED_S500``, ``RED_A700``). The unsuffixed name always + refers to the ``S500`` primary shade. + + Args: + name: Label for :attr:`~palettes.Palette.name`; defaults to + ``"MaterialDesign"`` when empty. + color_depth: Output format; see :class:`~palettes.Palette`. + swapped: Byte-swap 16-bit colors when ``True``. + color_map: RGB byte map; defaults to the built-in Material Design table. """ _shades = [ diff --git a/src/palettes/wheel.py b/src/palettes/wheel.py index b32fe81..e746d32 100644 --- a/src/palettes/wheel.py +++ b/src/palettes/wheel.py @@ -1,35 +1,43 @@ -""" -`pypalette.wheel` -==================================================== -This module contains the cool wheel color palette as a class object. - - -Usage: - from palettes import get_palette - palette = get_palette(name="wheel", color_depth=16, swapped=False, length=256) - # OR - palette = get_palette(name="wheel") - - # OR - from palettes.wheel import WheelPalette - palette = WheelPalette(color_depth=16, swapped=False, length=256) - - print(f"Palette: {palette.name}, Length: {len(palette)}") - for i, color in enumerate(palette): - for i, color in enumerate(palette): print(f"{i}. {color:#06X} {palette.color_name(i)}") - - # to access the named colors directly: - x = palette.RED - x = palette.BLACK - +# SPDX-FileCopyrightText: 2024 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""HSV and RGB color-wheel palettes. + +Build a smooth color wheel or fixed-saturation HSV ramp. Named Windows +16-color attributes (``RED``, ``BLACK``, etc.) are available on each +instance. + +Example: + >>> from palettes import get_palette + >>> palette = get_palette(name="wheel", length=256, saturation=1.0) + >>> palette[0] + >>> palette.RED """ from . import Palette as _Palette class WheelPalette(_Palette): - """ - A class to represent a color wheel as a palette. + """Color wheel or HSV ramp palette. + + With ``saturation=None`` and ``value=None`` (the default when both are + omitted), indices follow a classic RGB color wheel. When either + ``saturation`` or ``value`` is set, indices map linearly through hue at + fixed saturation and value. + + Args: + name: Prefix for :attr:`~palettes.Palette.name` (length suffix is added). + color_depth: Output format; see :class:`~palettes.Palette`. + swapped: Byte-swap 16-bit colors when ``True``. + cached: Memoize index lookups when ``True`` (default). + length: Number of colors in the wheel (default ``256``). + saturation: HSV saturation in ``0.0``–``1.0``, or ``None`` for + classic wheel mode. + value: HSV value in ``0.0``–``1.0``, or ``None`` for classic wheel mode. + + Raises: + ValueError: If ``saturation`` or ``value`` is outside ``0``–``1`` in + HSV mode. """ def __init__(