Month: August 2016

Your own web Python IDE

A few years back I was wondering about editing Python in the cloud when I first got a Chromebook. At the time web based IDEs were only just starting to go mainstream; they have flourished since. How much better would it be if you could set up your own server? Writing a code editor in JavaScript is a huge undertaking but there are tools out there to do this and once you have the code the rest should be straight forward.

I wanted to try out Ace code editor for a few reasons; it is mature, supports Python (along with every other language I’m bothered about), is customizable, extensible, is licensed under a BSD licence, has an API and seemed easy to set up. So I set about creating a test rig so I could investigate the code editor with. Just getting the code editor to display without an absolute window position proved tricky (it’s always the little things that take the time) but eventually I discovered the div tag needed a height property to work the way I wanted it to.

With the editor now displaying, I really wanted to get my rig to run the code and display the result so I knew everything was working. This prompted the executing code post a couple of days ago (now you know the project I was hinting at). With a working execute function that returned the output as a string I just needed to get the text out of Ace using the method provided and back to the server.

I originally tried to do this without using any other JavaScript libraries but soon realised this was adding a lot of unnecessary and distracting script to the web page so went back to using the ever useful jQuery library to deal with the post and the updating of the results div. Surprisingly it worked on the first go.

The code (in BitBucket due to the length) uses all of the executing arbitrary code lines from the previous post but with the test now replaced with a bottle web server and a 35 line single page web app. I mixed CSS and JavaScript in with the HTML to simplify the server side code not because I recommend this or would do this in reality.

Running the code should give you an editor similar to the one below.

pyedit

Advertisements

Executing arbitrary code

I last touched on executing code with the interactive console post but I’ve revisited the idea of entering Python code and executing it for a project I’ve been working on. Now before we start allowing users to execute code on your server is very dangerous. There is no way to make it 100% safe. If there is another way to achieve your aim without executing code then do it.

For an overview on what executing code (and importing modules) does behind the scenes, look at Armin Ronacher’s post on the subject. I will be following his suggestion of separating the compilation and execution steps. So, given the code we wish to run in a string, we will pass this to the compile function which returns bytecode. This bytecode can then be passed to the exec function to actually execute it. Just to be clear, we don’t have to split the process in two; you can run the Python code directly from a string as follows.

exec("print('Hello')")

So having decided to split the process into separate compile and exec steps, the first issue we need to solve is capturing errors (whether compile time or runtime) and passing these back to the user. Placing the compile and exec function inside a try block is enough to detect an error and we can use the format_exc function from the traceback module to send a formatted exception message back to the user if the code causes and exception.

Now we have a robust compile and execute sequence we just need to get the results of a successful run back to the user. We could create a log type function but Jochen Ritzel posted a simple context manager to redirect the standard output. With this we can redirect the stdout to a string (actually a StringIO) and return this assuming everything runs fine.

Putting all this together gives us the final code. The function exec_code works like exec but returns exceptions or the stdout of the executed code to you as well. The last three lines are just test code to show the difference between compile time, runtime and code with no errors.