Skip to content

GSoC'16 Bone101: Python Integration Phase

Amr Ragaey edited this page Aug 20, 2016 · 2 revisions

BeagleBoard GSoC'16: Improving Bone101 Experience

This page is written as part of documenting GSoC'16 project Improving Bone101 Experience. Reference: https://github.com/amragaey/bone101/wiki/BeagleBoard-GSoC'16:-Improving-Bone101-Experience

Python Integration Phase

User Interface

Repository : https://github.com/amragaey/bone101/tree/python-integration

Python Example UI In bone101, I created a page to run python examples #a867565.

The ace-editor in bone101, was initially supporting Javascript syntax. I added support for Python syntax #58de4a5.

The page contains three text areas; the script code (in Python) , standard input (if user waits for some input in the program), output (prints stdout if run successfully or stderr if anything went wrong), and the run and restore buttons that will be used to start and stop the execution of the python script.


The Python Runner Script

Repository: https://github.com/amragaey/bonescript/tree/python-runner

How is the python script executed on BeagleBone?

Let’s have a look into the Python Runner module written in bonescript. https://github.com/amragaey/bonescript/blob/python-runner/src/python_runner.js

The scenario

  1. User sends the code and standard input(if exists) to bonescript python runner.
  2. Server creates a temporary folder with random number on beaglebone to save the code and user inputs to it, and will contain the output file after execution complete.
  3. Server create a new file called ‘script.py’ and saves the code into it, and saves the standard input into another file called ‘inputFile’. ‘Let’s assume a folder now called ‘8935582107aadd1a9f1d’ contains two files script.py and inputFile’
  4. The python file is executed on beaglebone terminal with proper arguments, and the execution result is written to output.text file. ‘The folder ‘8935582107aadd1a9f1d’ contains now output.text that contains the execution result ’ .
  5. The output file contents is then sent back to the user.
  6. If user clicks on ‘reset’ button, the process terminate, and the temporary folder is removed.

I used a temporary folder as wrapper for each python script files to guarantee uniqueness of file names and avoid any overrides/or conflicts

A look into the code

The design of the runner is based on main function pythonRunner and sub-functions attached to its prototype. The use of prototype here is to make the design more like an OOP design, the variables of main function is easily accessible to all sub functions, what makes the code easier to read and to scale.

The constructor pythonRunner constructor takes five arguments (path, folder, file, code, stdin)

  • path: is the path used in which we create our temporary folder. ‘I assume it to be
  • folder: the name of the temporary folder that is chosen by a random function.
  • file : the file that will contain the python script. ‘ex.script.py’
  • code: the actual python code written by the user.
  • stdin: the standard input sent by the user (if exists).

pythonRunner.writeFiles(callback) This method is responsible for creating the temporary folder inside the specified path, the folder is created using child_process.exec() method, on the call back of exec(), the python code is written into a file ‘ex.script.py’ , and the stdin is written into ‘inputFile’, both files are saved in the temporary folder.

pythonRunner.executeScript(callback) This method is responsible for the actual execution of the python script. The command used to run the script is: Python script.py -<inputFile 2>&1 | tee -a output.text “inputFile and output.text used without their path here to simplify the command, they should be referred to temporary folder as seen in code”

  • The first argument -< inputFile is responsible to pass the standard input to the python script during execution (if available). If the user writes for example python code: raw_input(“enter name: “), the inputFile should contain a name like ‘Amr’.

  • The second argument 2>&1 | tee -a output.text is responsible for returning the standard output or the standard error to a file called ‘output.text’, the file will be created after execution, and the contents of the file is returned in the callback of the exec() function to the user. If the output file isn’t created for any reason, an error message will be printed to the JavaScript console. The exec() function is assigned to variable called ‘c’. That will be used in killScript function to kill the process later.

pythonRunner.killScript(callback) This method is responsible for killing the running python process, and removes the temporary folder with its contents.

Using child_process.exec() to run the command rm -rf /temporary folder, and on the callback of the exec() the process is killed using c.kill(), a message will be returned in callback that “process is terminated”.

Integration to Bonescript/ Issues?

I had some issues with my beaglebone, and wasn't sure how to start this phase. it also seemed that debugging bonescript code isn't straightforward. As to save time I started writing this module. I believe that separate functions may not do the job as good as the module does.

After discussion with Jason Kridner, I see that including the Python Runner in bonescript would require some extra steps in bonescript code. Here is an example mentioned by Jason on IRC on how to get to a module remotely that requires an object to be created/opened, https://github.com/amragaey/bonescript/blob/python-runner/src/my.js#L355-L396 , it's fairly complex example of creating a wrapper for a remotely created instance, it maintains an array of open ports, and this is an example of how it is used https://github.com/amragaey/bonescript/blob/python-runner/src/iic.js, and here https://github.com/amragaey/bonescript/blob/python-runner/src/serial.js. The 'port' is a required argument and is what uniquifies the instance. I think that the temporary folder name is what uniquifies pythonRunner instance.

Testing

For the sake of testing the python runner is tested with Express, a NodeJS web framework, just to make sure functions work fine and the output returns as expected to the user. The test is done with simple two API requests.

  • Post request with constructor attributes ‘written manually’.
  • Get request to kill the process from the client/browser side.

The source code of this version is available at : https://github.com/amragaey/python-runner

Features of Python Runner

  • Python runner is written similar to OOP programs even Javascript isn’t object oriented. The code could be extended easily, and any feature could be attached as a method to PythonRunner prototype.
  • Python Runner provides a wrapper for the files which is the temporary folder to save scripts from conflicts or overrides.
  • PythonRunner could be used also to run Ruby scripts, just by changing the file name to ‘script.rb’ and let the argument of the execution being ‘ruby script.rb --args’ instead of ‘python script.py --args’. The language could be passed as an attribute to the constructor to make the pythonRunner more generic.