Patchwork [RFC] devshell: Add interactive python shell

login
register
mail settings
Submitter Richard Purdie
Date May 19, 2013, 10:28 a.m.
Message ID <1368959294.32727.102.camel@ted>
Download mbox | patch
Permalink /patch/50111/
State New
Headers show

Comments

Richard Purdie - May 19, 2013, 10:28 a.m.
Being able to interact with the python context in the Bitbake task execution
environment has long been desirable. This patch introduces such a
mechanism. Executing "bitbake X -c devpyshell" will open a terminal connected
to a python interactive interpretor in the task context so for example you can
run commands like "d.getVar('WORKDIR')"

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
I've marked this as RFC since this is not production ready at this
point. It does illustrate what we could possibly do for an interactive
python shell though. Usage is "bitbake bash -c devpyshell" and then you
can type 'd.getVar("WORKDIR")' into the prompt.

Cleanup needed:

 * The formatting of the prompt and return data is ugly and needs 
   correctly handling.
 * Shutdown is broken as there is a hanging python child process left 
   around you have to kill manually before you can restart bitbake.
 * d.keys() crashes the terminal. Large data transfer breaks it?
 * Would local=locals() get us the correct context rather than building 
   manually?

Whether I'll find time to fix these things I don't know, is anyone
interested in working on this?

Patch

diff --git a/meta/classes/devshell.bbclass b/meta/classes/devshell.bbclass
index a780118..8a54b74 100644
--- a/meta/classes/devshell.bbclass
+++ b/meta/classes/devshell.bbclass
@@ -25,3 +25,35 @@  python () {
             d.appendVar("OE_TERMINAL_EXPORTS", " " + k[0])
        d.delVarFlag("do_devshell", "fakeroot")
 } 
+
+python do_devpyshell() {
+    m, s = os.openpty()
+    sname = os.ttyname(s)
+    os.system('stty cs8 -icanon -echo < %s' % sname)
+    pid = os.fork()
+    if pid:
+        oe_terminal("oepydevshell-internal.py %s" % sname, 'OpenEmbedded Developer PyShell', d)
+        os._exit(0)
+    else:
+        os.dup2(m, sys.stdin.fileno())
+        os.dup2(m, sys.stdout.fileno())
+        os.dup2(m, sys.stderr.fileno())
+        bb.utils.nonblockingfd(sys.stdout)
+        bb.utils.nonblockingfd(sys.stdin)
+
+        _context = {
+            "os": os,
+            "bb": bb,
+            "time": time,
+            "d": d,
+        }
+
+        import code
+        try:
+            code.interact("BB: ", local=_context)
+        except Exception as e:
+            bb.fatal(str(e))
+}
+addtask devpyshell after do_patch
+
+do_devpyshell[nostamp] = "1"
diff --git a/scripts/oepydevshell-internal.py b/scripts/oepydevshell-internal.py
new file mode 100755
index 0000000..946f562
--- /dev/null
+++ b/scripts/oepydevshell-internal.py
@@ -0,0 +1,32 @@ 
+#!/usr/bin/env python
+
+import os
+import sys
+import time
+import select
+import fcntl
+
+def nonblockingfd(fd):
+    fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
+
+if len(sys.argv) != 2:
+    print("Incorrect parameters")
+    sys.exit(1)
+
+try:
+    print(sys.argv[1])
+    pty = open(sys.argv[1], "w+b")
+    nonblockingfd(pty)
+    nonblockingfd(sys.stdin)
+    while True:
+        (ready, _, _) = select.select([pty, sys.stdin], [] , [], 1)
+        if pty in ready:
+            r = pty.read()
+            sys.stdout.write(r)
+        if sys.stdin in ready:
+            r = sys.stdin.read()
+            pty.write(r)
+except Exception as e:
+    print(str(e))
+    time.sleep(5)
+