# Tuesday, November 11, 2008
« Generating test data with IronPython ins... | Main | Wasting 2 Hours »

The other day I asked a coworker whether one our proprietary GUI based tools had a command line interface, to which he replied no.  I said that's cool, we don't really need one to run the tool headless from the command line anyway.  Command lines are antiquated.

Before the days of Windows command shells were the only way to run applications.  Command lines allow us to pipe input and output, batch programs, and otherwise automate repetitive tasks.  In *nix-land they are especially sharp tools.  However they are not without their own problems, foremost in my mind is argument processing.  Using a command line version of an application can be a pain, especially for any non-trivial app. 

If you look at any serious application that allows automation, they have their own automation framework.  Office allows you to write VBA, .NET, and also exposes a COM automation interface.  Maya has its own scripting language (Mel script, guess what Mel stand for) embedded in it along with the ability to run C++ plugins.  Now I bet you're thinking, yes big applications use automation frameworks and scripting languages, but I don't have that kind of time to invest in my little app.  Good, me neither.

In recent years Microsoft has provided less heavy handed approaches to consuming .NET assemblies. There are a couple different approaches that I can think of to use our .NET objects from the command line without writing a console application: Windows PowerShell and the DLR. 

PowerShell is more geared towads sysadmins, as such I like to use the DLR, more specifically IronPython.  Python is a very easy language to pickup, widespread (at least in the *nix world), and powerful.  I like to think of IronPython as C# without all the typing (OK, I stole that from Boo, but remember Boo syntax is based on Python).  Both have one important ability which we will need, the ability to use .NET assemblies and types from the command line (or script).

As an example I have a .NET library assembly that will run JSUnit tests, however I now need a way to run these unattended from the command line as part of a BVT.  Instead of writing a command line app for it I decide to write a quick little IronPython script which behaves very much like a console application.

import clr
import sys

clr.AddReferenceToFile("Sneal.JsUnitUtils.dll")
from Sneal.JsUnitUtils import *
from Sneal.JsUnitUtils.Browsers import *
from System.Collections.Generic import List
from System.IO import Path

# usage
if sys.argv.Count < 3:
	print "Usage: ipy IPJsUnit.py c:\source\mywebroot jsunittest1.html jsunittest2.html"
	sys.exit(False)

# command line params
webRootDirectory = sys.argv[1]

testFiles = List[str]()
for arg in sys.argv[2:]:
	testFiles.Add(Path.Combine(webRootDirectory, arg))

# create an execute test runner
mgr = JsUnitTestRunnerFactory()
runner = mgr.CreateRunner(testFiles, webRootDirectory, With.InternetExplorer)
success = runner.RunAllTests()

# print all results
if not success:
	print "Test run failed."
	for error in runner.Errors:
		print error.TestPage
		print error.FunctionName
		print error.Timing
		print error.Message
else:
	print "Yay!  All tests passed."

You'll notice that the application in fact takes two+ command line parameters, the webroot and the test files.  The advantage of this script over a console application from my point of view is:

  1. Less code to write.
  2. Documentation by example.
  3. Starting script for an end user to modify to their needs.

Number 3 I think is particularly important.  Within a minute or two an end user can modify this script to their needs and desires.  They could hardcode the arguments if they wanted, or pass additional arguments without the headache of launching Visual Studio and recompiling a console application.  Almost anyone with a text editor could modify this and quickly get on with their life.

Here instead of a well meaning (but over-complicated) console application, its been replaced with just enough glue to bootstrap us into business.  Gluing components together is what scripting languages are good at, so lets take them into consideration instead of always reaching for another console app.

Monday, November 17, 2008 4:33:03 PM (GMT Standard Time, UTC+00:00)
Comment.
Sneal
Monday, November 17, 2008 7:55:22 PM (GMT Standard Time, UTC+00:00)
test comment
Ray Vega
Monday, November 17, 2008 7:59:13 PM (GMT Standard Time, UTC+00:00)
This might explain why Lua is very popular in the gaming industry for a lot of those same reasons.

I wholeheartedly agree. If you recall, I had initially create a .NET console app to merge the text of sql change script files. Since learning and using Python, I have create the equivalent in less time and code.

Creating console apps are too high friction for most tasks. With a scripting language, you can just immediately jump in and start writing (or modifying existing) code to perform some menial development task. I think this also encourages other developers to do the same if given the option of script files versus console apps. Like you said, the script code is more exposed and visible than with dealing with a compiled exe which raises the barrier of understanding for other devs.

Just to mention, another classic example of a "serious application that allows automation" with "their own automation framework" is Emacs which uses Elisp for extensibility and customization. (Check out this Steve Yegge blog post (http://steve-yegge.blogspot.com/2007/01/pinocchio-problem.html) on what he thinks are the characteristics of a "serious application" in which he mentions built-in scripting. But, like you said, you need to invest a lot of time to develop that in addition to the app itself)
Thursday, November 20, 2008 7:19:04 PM (GMT Standard Time, UTC+00:00)

"Within a minute or two an end user can modify this script to their needs and desires ... Almost anyone with a text editor could modify this and quickly get on with their life."


Your end user must be a different caliber of a user than most of mine.

RyanB
RyanB
Comments are closed.