Repository: ManiacalLabs/BiblioPixel
Pull request #934 (2018-08-21 12:01:49)

Recognize class names in command line
From docs work.

This is very slick - now you can call animations from the command line by their typename!

Just write:

    bp BiblioPixelAnimations.matrix.Twinkle

or

    bp '$bpa.matrix.Twinkle'

or

    bp @bpa.matrix.Twinkle

Works with `+` notation, etc.

So now you do `bp animations` and then pick one to run - done.

adammhaile: +1

On Tue, Aug 21, 2018 at 8:01 AM Tom Swirly <notifications@github.com> wrote:

> From docs work.
>
> This is very slick - now you can just write:
>
> bp -s BiblioPixelAnimations.matrix.Twinkle
>
> or
>
> bp -s '$bpa.matrix.Twinkle'
>
> or
>
> bp -s @bpa.matrix.Twinkle
>
> Works with + notation, etc.
> ------------------------------
> You can view, comment on, or merge this pull request online at:
>
>   https://github.com/ManiacalLabs/BiblioPixel/pull/934
> Commit Summary
>
>    - Recognize class names in command line
>
> File Changes
>
>    - *M* bibliopixel/main/run.py
>    <https://github.com/ManiacalLabs/BiblioPixel/pull/934/files#diff-0>
>    (24)
>    - *M* bibliopixel/project/fill.py
>    <https://github.com/ManiacalLabs/BiblioPixel/pull/934/files#diff-1>
>    (8)
>
> Patch Links:
>
>    - https://github.com/ManiacalLabs/BiblioPixel/pull/934.patch
>    - https://github.com/ManiacalLabs/BiblioPixel/pull/934.diff
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/ManiacalLabs/BiblioPixel/pull/934>, or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AA6a6tMwfB-EJNXK7fILQ9DHabTk-Jlcks5uS_augaJpZM4WFowU>
> .
>

rec: Ah, breakage, one moment... :-/

Patch:
diff --git a/bibliopixel/main/run.py b/bibliopixel/main/run.py
index 89c9f95..8992bbc 100644
--- a/bibliopixel/main/run.py
+++ b/bibliopixel/main/run.py
@@ -2,11 +2,12 @@
 Run specified project from file or URL
 """
 
-import os, sys, time, traceback
+import os, string, sys, time, traceback
 from . import common_flags, simpixel
 from .. util import data_file, log, pid_context, signal_handler
 from .. animation import Animation
 from .. project import load
+from .. project.aliases import ALIAS_MARKERS
 from .. project.project import Project
 
 RUN_ERROR = """When reading file {filename}:
@@ -87,9 +88,14 @@ def _get_projects(args):
                 elif '{' in filename:
                     desc = data_file.loads(filename)
                 else:
-                    desc = load.data(filename, False)
-                    desc = data_file.load(desc, filename)
-                    root_file = os.path.abspath(filename)
+                    try:
+                        desc = load.data(filename, False)
+                        desc = data_file.load(desc, filename)
+                        root_file = os.path.abspath(filename)
+                    except:
+                        if not _might_be_classname(filename):
+                            raise
+                        desc = {'animation': filename}
 
                 if gif:
                     if 'filename' in gif:
@@ -220,3 +226,13 @@ def set_parser(parser):
 
 
 _RUNNING = False
+_ALL_CHARS = set(string.ascii_letters + string.digits + '._' + ALIAS_MARKERS)
+_START_CHARS = set(string.ascii_letters + '.' + ALIAS_MARKERS)
+_END_CHARS = set(string.ascii_letters + string.digits)
+
+
+def _might_be_classname(s):
+    return (s and
+            set(s) <= _ALL_CHARS and
+            s[0] in _START_CHARS and
+            s[-1] in _END_CHARS)
diff --git a/bibliopixel/project/fill.py b/bibliopixel/project/fill.py
index f6306ce..671c1d1 100644
--- a/bibliopixel/project/fill.py
+++ b/bibliopixel/project/fill.py
@@ -6,6 +6,7 @@ so they have exactly the right constructor.
 import copy
 from . import aliases, alias_lists, construct, merge
 from .. import layout, util
+from .. animation.strip import BaseStripAnim
 
 DEFAULT_DRIVERS = [construct.to_type('simpixel')]
 
@@ -26,11 +27,8 @@ def fill_layout(animation):
     # This is called from Project.__init__ - TODO: fix that.
     datatype = animation['datatype']
 
-    try:
-        args = datatype.LAYOUT_ARGS
-        layout_cl = datatype.LAYOUT_CLASS
-    except:
-        raise ValueError('Missing "layout" section')
+    args = getattr(datatype, 'LAYOUT_ARGS', BaseStripAnim.LAYOUT_ARGS)
+    layout_cl = getattr(datatype, 'LAYOUT_CLASS', BaseStripAnim.LAYOUT_CLASS)
 
     args = {k: animation[k] for k in args if k in animation}
     return dict(args, datatype=layout_cl)


Test patch:
diff --git a/test/bibliopixel/project/bad_project_test.py b/test/bibliopixel/project/bad_project_test.py
index ebc5375..f5fed8f 100644
--- a/test/bibliopixel/project/bad_project_test.py
+++ b/test/bibliopixel/project/bad_project_test.py
@@ -168,15 +168,11 @@ class BadProjectTest(unittest.TestCase):
             ('Unknown attribute for run: "bad_attribute"',))
 
     def test_missing_layout(self):
-        with self.assertRaises(ValueError) as e:
-            make(MISSING_LAYOUT)
-        self.assertEquals(
-            e.exception.args,
-            ('Missing "layout" section',))
+        animation = make(MISSING_LAYOUT, run_start=False)
+        self.assertEquals(animation.__class__.__name__, 'Off')
+        self.assertEquals(animation.layout.__class__.__name__, 'Strip')
 
     def test_missing_everything(self):
-        with self.assertRaises(ValueError) as e:
-            make('{}')
-        self.assertEquals(
-            e.exception.args,
-            ('Missing "layout" section',))
+        animation = make('{}', run_start=False)
+        self.assertEquals(animation.__class__.__name__, 'Animation')
+        self.assertEquals(animation.layout.__class__.__name__, 'Strip')
diff --git a/test/bibliopixel/project/project2_test.py b/test/bibliopixel/project/project2_test.py
index 5d40395..079382f 100644
--- a/test/bibliopixel/project/project2_test.py
+++ b/test/bibliopixel/project/project2_test.py
@@ -14,8 +14,7 @@ def classname(c):
 class Project2Test(unittest.TestCase):
     def test_empty(self):
         with patch.patch(defaults, 'BYPASS_PROJECT_DEFAULTS', True):
-            with self.assertRaises(ValueError):
-                project.project()
+            project.project()
 
     def test_single(self):
         source = {