tags:

views:

54

answers:

3

We're considering using Python (IronPython, but I don't think that's relevant) to provide a sort of 'macro' support for another application, which controls a piece of equipment.

We'd like to write fairly simple functions in Python, which take a few arguments - these would be things like times and temperatures and positions. Different functions would take different arguments, and the main application would contain user interface (something like a property grid) which allows the users to provide values for the Python function arguments.

So, for example function1 might take a time and a temperature, and function2 might take a position and a couple of times.

We'd like to be able to dynamically build the user interface from the Python code. Things which are easy to do are to find a list of functions in a module, and (using inspect.getargspec) to get a list of arguments to each function.

However, just a list of argument names is not really enough - ideally we'd like to be able to include some more information about each argument - for instance, it's 'type' (high-level type - time, temperature, etc, not lanaguage-level type), and perhaps a 'friendly name' or description.

So, the question is, what are good 'pythonic' ways of adding this sort of information to a function.

The two possibilities I have thought of are:

  • Use a strict naming convention for arguments, and then infer stuff about them from their names (fetched using getargspec)

  • Invent our own docstring meta-language (could be little more than CSV) and use the docstring for our metadata.

Because Python seems pretty popular for building scripting into large apps, I imagine this is a solved problem with some common conventions, but I haven't been able to find them.

+3  A: 

I would use some kind of decorator:

class TypeProtector(object):
    def __init__(self, fun, types):
        self.fun, self.types = fun, types
    def __call__(self, *args, **kwargs)
        # validate args with self.types
        pass
        # run function
        return fun(*args, **kwargs)

def types(*args):
    def decorator(fun):
        # validate args count with fun parameters count
        pass
        # return covered function
        return TypeProtector(fun, args)
    return decorator

@types(Time, Temperature)
def myfunction(foo, bar):
    pass

myfunction('21:21', '32C')
print myfunction.types
Tomasz Wysocki
Thanks for this - I wish I could give you all the 'answer'. Aside from the original question, what's the role of TypeProtector in this?
Will Dean
ie. you can easily check if function is decorated or not: isinstance(TypeProtector, o). Basically modifying function object directly like in `fcn.params = args` its not so good idea if you want clean self-explainable code.
Tomasz Wysocki
+4  A: 

Decorators are a good way to add metadata to functions. Add one that takes a list of types to append to a .params property or something:

def takes(*args):
    def _takes(fcn):
        fcn.params = args
        return fcn
    return _takes

@takes("time", "temp", "time")
def do_stuff(start_time, average_temp, stop_time):
    pass
Nathon
Consider using objects rather than strings for describing types:`@takes(Time, Temp, Time)`. Strings are not very good approach because you can't extend that model without modifying core. Also your core can by state-less (you don't have to register type names). Your objects should know how to render itself to HTML and how to validate itself. Also you have benefit that you can parametrize your types:`@takes(Int(max=100, min=0))`.
Tomasz Wysocki
Yes, that. The strings were really just demonstrative. When I did this in the past, I used functions that accepted strings (my only source of input) and returned objects that my program could use (sometimes after doing database lookups).
Nathon
+1  A: 

The 'pythonic' way to do this are function annotations.

def DoSomething(critical_temp: "temperature", time: "time")
    pass
leoluk
What about 2.6?
Tomasz Wysocki
for 2.6, he could use your one
leoluk
This was very interesting as it lead me into several useful sources of info - unfortunately I can't use it on this particular project because of the version issue, but I will eventually, I'm sure
Will Dean