Repository: ManiacalLabs/BiblioPixel
Pull request #608 (2018-02-03 19:26:45)

More docs
Colors.

You'll note that I just started on `util/thread` and then realized that there was too much stuff that no one cares about and is going away in 4.0 anyway.  We need a way to mark some directories as "not documented".

adammhaile: Fair

On Sat, Feb 3, 2018 at 2:33 PM, Tom Swirly <notifications@github.com> wrote:

> *@rec* commented on this pull request.
> ------------------------------
>
> In bibliopixel/util/colors/arithmetic.py
> <https://github.com/ManiacalLabs/BiblioPixel/pull/608#discussion_r165823143>
> :
>
> > @@ -1,10 +1,16 @@
>  def color_blend(a, b):
> -    """Performs a Screen blend on RGB color tuples, a and b"""
> +    """
> +    Performs a Screen blend on RGB color tuples, a and b
> +    DEPRECATED:  use numpy
>
> Actually - I'm going to eliminate these files from the .rst build entirely!
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/ManiacalLabs/BiblioPixel/pull/608#discussion_r165823143>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AA6a6ha6Ut3o078faAXCbBt2bf5dNMjPks5tRLR_gaJpZM4R4SuB>
> .
>

rec: All right, I tweaked it a bit more!

The docs look OK.  I can see a bunch of ways to make 'em better, but I want to get through getting at least superficial coverage on everything before going back and polishing.
rec: Are we good to go?  :-D
adammhaile: Yep, looks good.

On Sun, Feb 4, 2018 at 6:07 AM, Tom Swirly <notifications@github.com> wrote:

> Are we good to go? :-D
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/ManiacalLabs/BiblioPixel/pull/608#issuecomment-362898756>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AA6a6jztCkJM6BcAXJwnCmKWit1Sis2hks5tRY93gaJpZM4R4SuB>
> .
>


Patch:
diff --git a/bibliopixel/main/color.py b/bibliopixel/main/color.py
index 96a3d0e..8fad6e3 100644
--- a/bibliopixel/main/color.py
+++ b/bibliopixel/main/color.py
@@ -1,14 +1,13 @@
 """
-Convert between color names and color tuples
+Toggle between color names and color tuples
 """
 
-from .. colors import names
-from .. util import log
+from .. util import colors, log
 
 
 def run(args):
     if args.list:
-        log.printer(*sorted(names.COLOR_DICT.items()), sep='\n')
+        log.printer(*sorted(colors.COLOR_DICT.items()), sep='\n')
 
     if not args.colors:
         if not args.list:
@@ -17,7 +16,7 @@ def run(args):
     failures = []
     for c in args.colors:
         try:
-            log.printer(c, names.toggle(c), sep=': ')
+            log.printer(c, colors.toggle(c), sep=': ')
         except Exception as e:
             failures.append(c)
     if failures:
diff --git a/bibliopixel/util/colors/__init__.py b/bibliopixel/util/colors/__init__.py
index 893ebf9..0f31e59 100644
--- a/bibliopixel/util/colors/__init__.py
+++ b/bibliopixel/util/colors/__init__.py
@@ -50,7 +50,7 @@ from . conversions import hsv2rgb_spectrum, hsv2rgb_rainbow, hsv2rgb_360
 from . conversions import hsv2rgb, hue2rgb, hue_gradient, hue2rgb_360
 from . conversions import hue_helper, hue_helper360
 from . wheel import wheel_color, wheel_helper
-from . names import COLORS
+from . names import COLORS, COLOR_DICT, toggle
 
 # DEPRECATED methods and module name
 hex2rgb = COLORS.__getitem__
diff --git a/bibliopixel/util/colors/arithmetic.py b/bibliopixel/util/colors/arithmetic.py
index 94bd327..ac11a34 100644
--- a/bibliopixel/util/colors/arithmetic.py
+++ b/bibliopixel/util/colors/arithmetic.py
@@ -1,10 +1,14 @@
 def color_blend(a, b):
-    """Performs a Screen blend on RGB color tuples, a and b"""
+    """
+    Performs a Screen blend on RGB color tuples, a and b
+    """
     return (255 - (((255 - a[0]) * (255 - b[0])) >> 8),
             255 - (((255 - a[1]) * (255 - b[1])) >> 8),
             255 - (((255 - a[2]) * (255 - b[2])) >> 8))
 
 
 def color_scale(color, level):
-    """Scale RGB tuple by level, 0 - 256"""
+    """
+    Scale RGB tuple by level, 0 - 256
+    """
     return tuple([int(i * level) >> 8 for i in list(color)])
diff --git a/bibliopixel/util/colors/classic.py b/bibliopixel/util/colors/classic.py
index d429326..abccace 100644
--- a/bibliopixel/util/colors/classic.py
+++ b/bibliopixel/util/colors/classic.py
@@ -1,4 +1,9 @@
-# "Classic" colors from BP3.0.
+"""
+"Classic" colors from BP3.0
+
+This is a list of hardcoded colors as tuples from the original BP3.0.
+
+"""
 
 AliceBlue = (240, 248, 255)
 Amethyst = (153, 102, 204)
diff --git a/bibliopixel/util/colors/gamma.py b/bibliopixel/util/colors/gamma.py
index 61e12fc..c3a0d68 100644
--- a/bibliopixel/util/colors/gamma.py
+++ b/bibliopixel/util/colors/gamma.py
@@ -1,5 +1,15 @@
 class Gamma(object):
+    """
+    Compute a fixed gamma table with 256 entries.
+    """
     def __init__(self, gamma=1.0, offset=0, lower_bound=0):
+        """
+        :param float gamma: the root for gamma computation
+        :param float offset: an offset added to the result
+        :param int lower_bound: The lowest possible output value - the highest
+                                is always 255.
+
+        """
         self.gamma = gamma
         self.offset = offset
         self.lower_bound = lower_bound
@@ -11,6 +21,10 @@ class Gamma(object):
         self.table = tuple(gam(i) for i in range(256))
 
     def get(self, i):
+        """
+        :returns: The gamma table entry
+        :param int i: the index into the table
+        """
         return self.table[max(0, min(255, int(i)))]
 
 
diff --git a/bibliopixel/util/colors/juce.py b/bibliopixel/util/colors/juce.py
index 0d98d3a..6b330ff 100644
--- a/bibliopixel/util/colors/juce.py
+++ b/bibliopixel/util/colors/juce.py
@@ -1,7 +1,11 @@
-# These colors come from:
-# https://github.com/timedata-org/timedata/blob/master/src/cpp/timedata
-# /color/names_table_inl.h
-# which come from Juce: http://juce.com.
+"""
+A longer list of color names.
+
+These colors come from:
+https://github.com/timedata-org/timedata/blob
+    /master/src/cpp/timedata/color/names_table_inl.h
+which came from Juce: http://juce.com.
+"""
 
 COLORS = {
     'alice blue': 0xf0f8ff,
@@ -487,9 +491,11 @@ COLORS = {
     'yellow': 0xffff00,
 }
 
-# Some colors have more than one name.  This is a list of secondary names for
-# Juce colors.  We need this to reliably go from color back to color name and
-# get the same result each time.
+"""
+Some colors have more than one name.  This is a list of the secondary names for
+Juce colors.  We need this to reliably go from color back to color name and
+get the same result each time.
+"""
 SECONDARY_NAMES = {
     'aqua',
     'aquamarine 1',
diff --git a/bibliopixel/util/colors/names.py b/bibliopixel/util/colors/names.py
index a8c8bfa..bc2f5c4 100644
--- a/bibliopixel/util/colors/names.py
+++ b/bibliopixel/util/colors/names.py
@@ -1,58 +1,19 @@
 """
-To get a color from a name, use
-
-    COLORS.colorname
-
-or if the name is a variable or has a space in it,
-
-    COLORS['color name']
-
-To get a name from a color, use
-
-    COLOR((0, 255, 255))
+Convert back and forth between colors and string names.
 """
 
 import re
 from . import juce, classic
 
 
-def _to_triplet(color):
-    rg, b = color // 256, color % 256
-    r, g = rg // 256, rg % 256
-    return r, g, b
-
-
-def _classic_pairs():
-    find = re.compile(r'[A-Z][a-z0-9]+').finditer
-
-    for name in dir(classic):
-        if name == 'Green_HTML':
-            yield 'green html', classic.Green_HTML
-
-        elif len(name) > 1 and name[0].isupper() and name[1].islower():
-            key = ' '.join(i.group(0).lower() for i in find(name))
-            yield key, getattr(classic, name)
-
-
-def _from_number(s):
-    s = s.strip()
-    for prefix in '0x', '#':
-        if s.startswith(prefix):
-            return int(s[len(prefix):], 16)
-
-    return int(s)
-
-
-JUCE_COLORS = {k: _to_triplet(v) for k, v in juce.COLORS.items()}
-CLASSIC_COLORS = dict(_classic_pairs())
-COLOR_DICT = dict(CLASSIC_COLORS, **JUCE_COLORS)
-
-SECONDARY_NAMES = juce.SECONDARY_NAMES.union({'off', 'on'})
-TO_NAME = {v: k for k, v in COLOR_DICT.items() if k not in SECONDARY_NAMES}
-TO_COLOR = {k.replace(' ', ''): v for k, v in COLOR_DICT.items()}
-
-
 def name_to_color(name):
+    """
+    :param str name: a string identifying a color.  It might be a color name
+                     from the two lists of color names, juce and classic; or
+                     it might be a list of numeric r, g, b values separated by
+                     commas.
+    :returns: a color as an RGB 3-tuple
+    """
     def to_color(name):
         name = name.lower()
         if ',' in name:
@@ -81,6 +42,13 @@ def name_to_color(name):
 
 
 def color_to_name(color):
+    """
+    :param tuple color: an RGB 3-tuple of integer colors
+    :returns: a string name for this color
+
+    ``name_to_color(color_to_name(c)) == c`` is guaranteed to be true (but the
+    reverse is not true, because name_to_color is a many-to-one function).
+    """
     try:
         return TO_NAME[tuple(color)]
     except:
@@ -88,13 +56,22 @@ def color_to_name(color):
 
 
 def toggle(s):
-    """Toggle between a name and a tuple representation."""
+    """
+    Toggle back and forth between a name and a tuple representation.
+
+    :param str s: a string which is either a text name, or a tuple-string:
+                  a string with three numbers separated by commas
+
+    :returns: if the string was a text name, return a tuple.  If it's a
+              tuple-string and it corresponds to a text name, return the text
+              name, else return the original tuple-string.
+    """
     is_numeric = ',' in s or s.startswith('0x') or s.startswith('#')
     c = name_to_color(s)
     return color_to_name(c) if is_numeric else str(c)
 
 
-class Colors(object):
+class _Colors(object):
     def __getitem__(self, name):
         try:
             return name_to_color(name)
@@ -111,4 +88,68 @@ class Colors(object):
         return color_to_name(color)
 
 
-COLORS = Colors()
+def _to_triplet(color):
+    rg, b = color // 256, color % 256
+    r, g = rg // 256, rg % 256
+    return r, g, b
+
+
+def _classic_pairs():
+    find = re.compile(r'[A-Z][a-z0-9]+').finditer
+
+    for name in dir(classic):
+        if name == 'Green_HTML':
+            yield 'green html', classic.Green_HTML
+
+        elif len(name) > 1 and name[0].isupper() and name[1].islower():
+            key = ' '.join(i.group(0).lower() for i in find(name))
+            yield key, getattr(classic, name)
+
+
+def _from_number(s):
+    s = s.strip()
+    for prefix in '0x', '#':
+        if s.startswith(prefix):
+            return int(s[len(prefix):], 16)
+
+    return int(s)
+
+
+"""
+COLOR is a "magic" color name variable.
+
+ To get a color from a name, use ``COLORS.<colorname>`` - for example
+
+::
+
+    COLORS.red
+    COLORS.ochre
+
+or if the name is a variable or has a space in it,
+
+::
+
+    COLORS['violet red 4']
+
+To get a name from a color, use
+
+::
+
+    COLOR((0, 255, 255))
+"""
+COLORS = _Colors()
+
+
+_JUCE_COLORS = {k: _to_triplet(v) for k, v in juce.COLORS.items()}
+_CLASSIC_COLORS = dict(_classic_pairs())
+
+"""
+A dictionary of every color by name.
+"""
+
+COLOR_DICT = dict(_CLASSIC_COLORS, **_JUCE_COLORS)
+
+# TODO: integrate this better with COLOR
+_SECONDARY_NAMES = juce.SECONDARY_NAMES.union({'off', 'on'})
+TO_NAME = {v: k for k, v in COLOR_DICT.items() if k not in _SECONDARY_NAMES}
+TO_COLOR = {k.replace(' ', ''): v for k, v in COLOR_DICT.items()}
diff --git a/bibliopixel/util/threads/sub.py b/bibliopixel/util/threads/sub.py
index 8210971..0af9fba 100644
--- a/bibliopixel/util/threads/sub.py
+++ b/bibliopixel/util/threads/sub.py
@@ -13,19 +13,17 @@ def run(function, *args, use_subprocess=False, daemon=True, **kwds):
     """
     Create input, output queues, call `function` in a subprocess or a thread.
 
-    `function` is called like this:
-        function(input, output, *args, **kwds)
-
-    Args:
-        use_subprocess: if true, create a new multiprocess;  if false, create
-            a new thread
-        function: the function to call
-        *args, **kwds: arguments to the function.
-        daemon: is the thread or subprocess run as a daemon or not?
-
-    Returns:
-        a tuple with three elements: the subprocess or thread, an input
-        queue, and an output queue.
+    ``function`` is called like this: ``function(input, output, *args, **kwds)``
+
+    :param use_subprocess: if true, create a new multiprocess;
+                           if false, create a new thread
+    :param function: the function to call
+    :param daemon: is the thread or subprocess run as a daemon or not?
+
+    :param args: positional arguments to the function
+    :param kwds: keyword arguments to the function
+    :returns: a tuple with three elements: the subprocess or thread, an input
+              queue, and an output queue.
     """
     if use_subprocess:
         Creator, Queue = multiprocessing.Process, multiprocessing.Queue
diff --git a/gen_docs.sh b/gen_docs.sh
index cd6d5c3..c2982a2 100755
--- a/gen_docs.sh
+++ b/gen_docs.sh
@@ -4,7 +4,19 @@ scripts/extract_bp_help doc/The-bp-Command.rst
 pushd ./doc/
 
 # delete ./api/ dir if exists
-rm -rf ./api || true
-sphinx-apidoc -o api ../bibliopixel/ ../bibliopixel/main/
+rm -rf ./api/ ./_build/ || true
+
+sphinx-apidoc \
+    -o api \
+    ../bibliopixel/ \
+    ../bibliopixel/main/ \
+    ../bibliopixel/util/threads/ \
+    ../bibliopixel/util/colors/arithmetic.py \
+    ../bibliopixel/util/colors/conversions.py \
+    ../bibliopixel/util/colors/classic.py \
+    ../bibliopixel/util/colors/gamma.py \
+    ../bibliopixel/util/colors/juce.py \
+#
+
 make html
 popd


Test patch:
diff --git a/test/bibliopixel/colors/color_test.py b/test/bibliopixel/colors/color_test.py
index 615e76b..dae0ab7 100644
--- a/test/bibliopixel/colors/color_test.py
+++ b/test/bibliopixel/colors/color_test.py
@@ -24,8 +24,8 @@ class ArithmeticTest(unittest.TestCase):
 class ClassicTest(unittest.TestCase):
     def test_classic(self):
         # Make sure that the classic and new color names are the same.
-        cc = names.CLASSIC_COLORS
-        jc = names.JUCE_COLORS
+        cc = names._CLASSIC_COLORS
+        jc = names._JUCE_COLORS
         for name, color in cc.items():
             self.assertEquals(color, jc.get(name, color), name)