Showing posts with label python3. Show all posts
Showing posts with label python3. Show all posts

Monday, July 8, 2013

Automatically create a GUI for your argparse based command line tool

Problem

You are asked to create small tool in python to help someone out. Someone else comes along who wants to reuse the tool, but needs a minor variation on the theme. No problem, you think, I'll just add a command line option using python's argparse module.

Fast forward one month. The tool proves to be a success, but everyone wanted something slightly different, leading to 15 command line options. Now people start to complain that they lose oversight over the command line options. Other people see the potential in your tool but they are afraid of command lines altogether...

You realize you could please your users by giving them a UI to set up the command line options. But creating a UI is a lot of work... What now?

Argparseui to the rescue!

If you think you may get away with a zero-effort but ugly auto-generated UI, perhaps argparseui (python2.7 and python3, GPLv3) can be of use. Argparseui depends on PyQt.

As of yet it doesn't support all of argparse, nor does it officially support python3 (although basic use cases seem to work with python3), but it seems to scratch my itch.

Example code?

 Using argparseui is very similar to using argparse. Here's an example:

Screenshot?

This is a screenshot made on Linux, IceWm. On windows, the dialog will look like a windows dialog, and on Mac OsX it should look like a Mac OsX dialog...

Thursday, January 5, 2012

Capture colored console output in a tkinter window

The problem

I want to run a console script that emits ansi color codes to generate a colorful output and capture that colorful output in a tkinter window. Ideally I want this to work for programs like cmake or emerge which semi-intelligently switch off color codes if they detect they are not running on a terminal. This is all tested under linux. Some if it might work on windows too, but I haven't investigated.

Approach

I will show some increasingly sophisticated approaches using python 3.2 and try to outline their limits or problems. In order not to overload the presentation, this section details some functions that are used by the different approaches below. First of all in a file ansicolortext.py I have the following MIT licensed code. It is a tkinter Text widget that tranforms ansi color code sequences into tkinter color tags. Next, in a file timeout.py I have the following code, created by Matt Harrison under MIT license and posted as an ActiveState recipe: Finally, here's a function to create the user interface (a tkinter window with a resizable AnsiColorText widget)

Approach 1

If you google for this type of stuff almost always someone will come up with something as follows: This simple approach works beautifully for the example given (ls --color) and is highly recommended if it works for you. For some programs, however, this doesn't work. Many programs try to detect if they are running on a terminal or if they are part of a piped series of commands, and in the latter case suppress color codes. Examples of such programs include CMake and grep with the --color option. The end result is that your output shows up in black and white.

Approach 2, attempt 1

In order to fool programs like CMake or grep into producing color codes we need to pretend we are a terminal. The way to do this in linux is by using a pseudoterminal. According to wikipedia,
In some operating systems, including Unix, a pseudo terminal is a pseudo-device pair that provides a text terminal interface without an associated device, such as a virtual console, computer terminal or serial port. Instead, a process assumes the role of the underlying hardware for the pseudo terminal session.
Pseudoterminals can be accessed using Python's pty module. pty.openpty() opens a new pseudo-terminal pair and returns a pair of file descriptors (master, slave), for the master and the slave end, respectively. The terminal emulator process is associated with the master device and the shell is associated with the slave. The terminal emulator process (master) receives input from the keyboard and mouse using windowing events, and is thus able to transmit these characters to the shell (slave), giving the shell the appearance of the terminal emulator being an underlying hardware object. Any terminal operations performed by the shell in a terminal emulator session are received and handled by the terminal emulator process itself (such as terminal resizing or terminal resets). This approach looks clean and simple, but it has a problem: os.read blocks when no more data comes in, and so our script hangs before the UI can even come up. One proposal could be to use a timeout when reading data using os.read.

Approach 2, attempt 2

This approach now deals with the blocking: upon a timeout the communication is stopped. It suffers from a different problem though: the UI doesn't appear before all data has come in. In the case of a CMake script this can take minutes to hours to complete. Not exactly what we wanted... therefore we need a slightly smarter approach.

Approach 2, attempt 3

The idea is this: we will run all the communication in a thread of its own. The UI will run in the main thread. The communication thread will send the data it receives to the main thread by means of a Queue object. The main thread will read data from the Queue and display it in the text widget as it receives it.

The TextUpdater object T lives in the program's main thread. It reads data from the communication thread t by means of Queue q and sends it to the Text widget text, which itself is a child widget of the Tk root window root. The TextUpdater thread is asked to start working 100ms after root.mainloop() is started. Like that the UI has ample time to get itself ready for processing. The communication thread ThreadCommand t opens a pseudoterminal and uses it to communicate with the program being run. It stops itself and flags itself as not running anymore if no more data comes from the process (i.e. if the reading timed out). Reading data from the process uses a version of os.read with a timeout added to it (read2). The data read by read2 is slightly processed before it is sent to the communication thread: only complete lines of text are sent to avoid that we break up text in the middle of a color code (this would give trouble because the AnsiColorText widget isn't smart enough to deal with half-finished color codes).

If the timeout value used in read2 is too small, the timeout can occur too soon (especially with processes that take some time to generate output, like CMake) so this part is still a weak spot in this approach. Too long a timeout causes one to wait long after the process finishes to really close the communication thread. If you know of better ways to handle this, please comment! After text is written to the Text widget, we call root.update() to give Tk() the chance to handle pending events. This makes sure that the text widget is updated as input comes in, and it also makes sure that you can resize the window or scroll through the displayed data while data is still coming in.

An alternative without timeout decorator

As I found out today, there's an alternative without timeout decorater, by using the poll call of Python's select module. It still uses a timeout mechanism to decide if there's data to be read. This time the timeout mechanism is provided by the select module directly.

Screenshot

Here's a screenshot of CMake producing colored output in a Tk window. While CMake is running, the window can be resized and you can navigate through it with arrow/pageUp/pageDown/... keys.

Saturday, December 24, 2011

Running YouNeedTests on windows xp with visual studio express 2010

Tutorial: running YouNeedTests on windows xp

The problem

I've updated YouNeedTests, my cross-platform tool for extracting and building C++ unit tests embedded in source code comments, to make it usable on Microsoft windows systems too. Here's a basic tutorial on how to set it up and get started on windows.

The tutorial

Installation

YouNeedTests depends on the following tools:
  • Python 3.2 (or newer): get it from the python website.
  • PyYaml: get it from the PyYaml website. I've used version 3.10
  • Mako: get it from the mako templates website. I've installed the latest available version 0.5.0 using Distribute.
  • CMake: get it from the cmake website.
  • YouNeedTests: you can either install git and clone the gitorious repository (this is easy via the installshield of git extensions) or you can head over to the gitorious repository and download the master branch as a tar.gz package (then you will also need a tool like 7-zip to unpack the package).
  • A C++ compiler. Strictly speaking, this is optional, but obviously YouNeedTests won't be very useful without one :) I've installed visual studio 2010 Express edition (free download) from the microsoft website.

Note:the google test framework is included in the YouNeedTests tool.

Building and running some tests

YouNeedTests comes with a run.bat script that runs YouNeedTests on some sample testinputs folder.

  • In a first step, run.bat will delete the testoutput folder if it already exists.
  • In a second step, "The "run.bat" will create a testoutput folder with a file called CMakeLists.txt and a series of folders containing automatically generated code (one folder per testsuite). Run.bat is a very short file, and I encourage you to take a look (and correct the paths to your python installation if needed).
  • In a third step, run.bat will change the working directory to the testoutput folder and try to run CMake on it, in order to generate the visual studio solution. CMake also supports many other compilers - run
    cmake --help
    to get an overview of supported environments.

When run.bat has finished, you should find a lot of files in your testoutput folder (imagine having to create all those by yourself to get a feeling for what YouNeedTests could do for you). You want to open the ALL_TESTS.sln file in Visual Studio 2010 Express. Inside the solution you will find different projects.

  • The ALL_BUILD project: build this to build the googletest framework and the tests extracted from the c++ comments.
  • The ALL_TEST project: build this to run all the tests
  • The t1, t2, t3, ... projects: setting one of those as startup project will run only that testsuite. Running a testsuite separately like this allows you to get much more detailed test output. Unfortunately, the console window containing the test results quickly disappears when the test finishes. You may want to run the test executable from a dos window instead so you can inspect the results.

Troubleshooting

If you run the sample tests by building the ALL_TEST project, but you get errors like

Could not find executable C:/development/youneedtests/youneedtests/testoutput/t4Avg/t4Avg
, it means you forgot to build the ALL_BUILD project first. Right-click ALL_BUILD in the solution explorer in visual studio, and click Build.

If you successfully built the ALL_BUILD project, but while running the sample tests included with YouNeedTests all tests fail with a reason like "OTHER FAILURE", it means that the gtest.dll was not found. In that case you should add the

youneedtests\testoutput\googletest\Debug
folder (use the full path as applicable on your sytem) to your PATH (right click
My Computer
, then click
Properties
, click
Advanced
, click
Environment Variables
, and in the
System Variables
group box, add the folder in the
Path
variable.) An alternative would be for YouNeedTests to copy the .dll files into each of the testsuite folders, but I'd rather not do that (the more tests you have, the more copies of gtest.dll are needed).

Monday, December 19, 2011

YouNeedTests: a python3 based C++ unit testing tool

(Image retrieved from http://blog.hinshelwood.com/. According to google image search this image is labeled for reuse. Please let me know if you disagree.)

Unit testing in C++

Perhaps you recognize the following story. You're finished writing C++ code, and now you want to add some unit tests, because it makes the metrics look good. You're using a well-established C++ unit testing framework, like CppUnit. Adding a new test requires manually adding files to the solution, adding seemingly duplicated information in .cpp and .h files (i.e. implementations and declarations) and invoking obscure macros. Or perhaps you copy an existing test file and start editing it to test new stuff. Or worse: you extend an existing test with some extra lines to avoid creating all the boilerplate code over and over again.

However, being a programmer and not a file copier, I would really appreciate if we could use our computer a bit more efficiently to specify tests.

YouNeedTests: Python3's doctest meets fitnesse for C++ code

doctest? fitnesse?

The brilliant thing about python's doctest module is how it extracts tests from comments embedded in the source code. This leads to executable documentation which by its very nature never needs to become outdated. Doctest is typically used to write unit tests: tests written by programmers for programmers, trying to test little bits of code in isolation.

The brilliant thing about fitnesse is how it specifies different test scenarios in table form. Fitnesse is typically used to define acceptance tests: tests written by business and QA people and intended for business and QA people. Acceptance tests usually involve more objects than unit tests.

requirements

I wanted a tool that could be used with C++, and that transforms comments embedded in the C++ code into unit tests. I also wanted a way to specify different test scenarios in table form. Because I prefer python over C++ in matters of text processing, I decided this was an ideal opportunity to wet my feet in several "hot" and "established" technologies:

  • git as source code management system. The brilliant thing about git is its distributed model, its insane flexibility, speed and compactness, and its superb support for branching and merging.
  • python3, the somewhat controversial, non-backward compatible successor to the very popular python2 language. I still have to find out what's so brilliant about python3 - but so far python3 hasn't been a negative experience.
  • yaml to format the comments from which the code will be generated. The brilliant thing about yaml is how it combines expressiveness of XML with a much more human friendly syntax. Human friendly enough that I didn't create a domain specific language to specify tests in.
  • googletest as backend unit testing framework. The brilliant thing about the googletest framework is how it requires fairly little boilerplate code to write simple tests. The second brilliant thing about googletest is that it supports death-tests, i.e. you can check if a piece of code crashes as expected.
  • CMake for the build system. The brilliant thing about CMake is how cross-platform, easy to use and versatile it is as a build system. It also comes with built-in provision for testing (and loads of stuff I haven't discovered yet).
  • mako to generate boiler plate code and build scripts. The brilliant thing about mako templates is their ease of use and the fast speeds with which they are rendered.

Example

Here's a simple comment that will 6 independent tests from a table of scenarios. Not all tests need to have a table, one can also embed raw code if desired. A comment like the above consists of several parts:
  • a testsuite name
  • a testsuite type: for now 3 types are supported:
    • COLUMNBASED: CODE section contains a list of tables. Each line in each table is an independent test.
    • ROWBASED: CODE section contains a list of tables. Each table becomes a test. Each line in a table is one step in the test.
    • RAW: CODE section contains normal C++ code.
  • a LINK section: this specifies which .cpp files should be linked in with the test
  • an INCLUDE section: this specifies which .h files to include in with the test
  • a STUBS section: this can contain arbitrary C++ code to resolve linker errors. Only stub those parts of the code which are not testing. There's no point in testing stubs :)
  • a PRE section: code in the PRE section is executed before anything from the table is execute
  • a POST section: code in the POST section is executed after executing statements from the table, but before executing assertions from the table
  • a CODE section. In case of a COLUMNBASED or ROWBASED test suite, the CODE section contains a list of tables. Each table has a table header that specifies code templates with placeholders $1, $2, .... The placeholders will be filled in with values from the table cells in the same column. Each table has a NULL column (the ~ symbol) which separates the statements on a line from the assertions.
    • The line containing the string 'twovalues' in table 'TABLE1', e.g. will result in the following lines of code being generated: where the double comma (",,") causes the code template to be unrolled. (Of course the system will also generate all the rest of the required boilerplate code and buildscripts, finally leading to this .cpp file (buildscript not shown here): Despite being used with a toy example, the table format already results in quite some space and boiler-plate reduction :)
All the generated tests can be run at once by issuing "make test" in the output folder:
More detailed test results are available by individually running tests:

Proof of concept code!

Got curious? Hop over to Gitorious! LGPL'd proof of concept code - for now I only tried to use it on debian sid amd64.