self vs. @ (Python and Ruby)

March 2, 2006

Until a few days ago, I considered myself a “faithful” Pythonist. I liked ObjectiveC. I liked a bunch of other languages (notably Prolog or Haskell). I quite liked C++ too. But in fact my true love was Python.

I know it is strange to talk about “love”. That is of course unproper. Unfortunately I’ve no time to find a better word to express the thing. Let say that I liked to code in Python independently of what I was coding. Quite the same thing than I love using MacOS independently of the task, while I may be forced to use Windows because of the task, but would not use it if it was up to me (few… complicated period).

Today I read this (pointles) discussion about “self” in Python. There are people who

  • Does not like to self.foo
  • Does not like to
    def foo(self, else):
      # code
    

I perfectly understand this position. In fact I do not really like having to reference self explicitly everytime (even if I fully understand why python does this). Of course it makes sense.

I’m calling methods /accessing variables of an object. So I should use the conventional way object.method|variable. There should be only one way to do it. For example Java “optional” this sucks. At least in my opinion. It has no purpose in method/variable stuff. Some use it to make clear that they’re referencing instance variables, some use a m_var notation.
If you are a C++ programmer you could be using var_ or (if you haven’t read the standard quite recently) _var

Of course having a clear and readable way to distinguish instance variables methods is good. That is clear. It makes you easier to read.

self is boring. I often forgot it and got error messages (about the ninth consecutive programming hour this is not the worst thing I do, however). In this sense I also forget the ruby @. And it’s better a spectacular error than a hidden bug. So… go on

I quite don’t like having specify self among the formal parameters. You schiznick, don’t you know its a method? Actualy Python does not. If you take a normal function and bind it to an object, the first formal parameter is the object itself. So its better that function was meant from the beginning that way and had a starting self parameter.

Of course this is boring, but its necessary and has not real disadvantages (a part from some additional typing.. something that with a decent editor is not a concern Aquamacs/Emacs or TextMate strongly advised).

And so all the knots get back to the comb. Python has this boring self everywhere. And it is there and should be there. Ruby hasn’t.
The @ makes the code quite readable (expecially with a decent editor). Of course it prevents name clashes and such. Having not to pass self to functions also makes it easier to refactor from functions to methods (ok, we know, in ruby every function is a method, but that’s not the point). This in the case that the method uses no instance variables.

But…

But Ruby treats variables and methods differently. Instance variables need a “special” syntax. Methods don’t. It’s not clear if a method is a function or not (of course it does the Right Thing)

    irb(main):001:0] def foo; "foo"; end
    =] nil
    irb(main):002:0] class Bar; def foo; "dont foo" ; end
    irb(main):003:1] def bar; foo; end
    irb(main):004:1] end
    =] nil
    irb(main):005:0] b = Bar.new
    =] #
    irb(main):006:0] puts b.bar
    dont foo
    =] nil

As I said it does the Right Thing… I’m just talking about readability. Of course you can use self in ruby too… but well. This is something I would have preferred solved in another way, even if right at the moment I’m find quite acceptable to renounce to have “one way” for a bit more pragmatism.


Ruby vs. Python? [no, Ruby vs. Ruby ]

February 20, 2006

In fact this is not a Ruby vs. Python list. I know not enought Ruby and I love Python to much :). It’s more a picture of the first impressions I had on Ruby after I seriously began studying it.

This is a quick list of thoughts. I’m probably adding more stuff later. In fact I’m reaaly amazed. Ruby is really hackish, but it’s also neat and clean. It may sound strange… but I’m afraid I’m gonna love ruby more than Python: it addresses many of the things I come to dislike in Python.

Some stuff I like

  • Adding dynamically stuff to classes. The syntax is clean and obvious
    class MyClass
      def aMethod
        puts "Called aMethod"
      end
    end
    
    m = MyClass.new
    
    begin
      m.aMethod2
    rescue NoMethodError => ex
      puts "Failed to call #{ex}"
    end
    
    class MyClass
      def aMethod2
          puts "Called aMethod2"
      end
    end
    
    m.aMethod2

    In Python for example it is not that clean. The ruby code looks more like ObjectiveC categories (even if in fact you don’t specify a category, of course, since ruby does not need categories).

  • private, protected, public modifiers. Being dynamic they do not limit me in any way (if I need I can dynamically change this)
    class MyClass
      protected
      def aMethod
        puts "Called aMethod"
      end
    end
    
    m = MyClass.new
    
    begin
      m.aMethod
    rescue NoMethodError => ex
      puts "Failed to call #{ex}"
    end
    
    class MyClass
      public :aMethod
    end
    
    m.aMethod

    Moreover I can’t think to a cleaner syntax to do this. Of course one can argue that they aren’t really necessary. Of course, but if you want they do give you a little bit of control, but they don’t limit you in any way.

  • Object#freeze: if you suspect that some unknown portion of code is setting a variable to a bogus value, try freezing the variable. The culprit will then be caught during the attempt to modify the variable.
  • I love postfixed control clauses. In fact if used with moderation they can augment code readbility (and of course if abused they make it less readable). However, it is pretty logigal to say do_something if something_else. In fact since ruby has blocks it is ok also to write
    begin
        # some stuff
    end if condition
    

    maybe not everyone agrees

  • Accessors are great. Using attr and similar constructs is great. I also find quite more readable to “assign” with foo= than using more Javesque setFoo(val). In fact properties in Python work in a similar fashion, even if maybe a bit more verbose (however, I think they make it clearer how to document code, but this is a ruby aspect I’ve not yet exploited)
  • Gems: I think python needs something like that. Pimp is not that good, in my opinion, of course.

Stuff I don’t know if I like or not

  • Not having to write parentheses with no-argument methods. I’ve not yet discovered if I like it or not.
  • All the $_… They are used a lot in Perl. I like it, but I’m afraid it can mess things up.

Stuff I dislike

  • I don’t like passing strings to require. In fact it can have advantages, but I prefer the pythonic import foo to require "foo".
  • The try catch semantic of try/catch, makes me think to some kind of disguised goto. I’m sure I’m wrong… but
  • I’d like to have something like Perl use strict;. It’s something I miss in Python (even if with pychecker you can just live without it). Now I’ve got to find some kind of “use strict” or “rubycheck”.
  • Assignment is an expression. This directly leads to code like
    a = true
    b = false
    print b if b=a
    

    and we imagine the programmer wanted to write

    a = true
    b = false
    print b if b==a
    

    However in Ruby almost everything is an expression, and it has quite a lot of advantages. So we have to tolerate the “=” for “==” problem.


Apple PyObjC tutorial (Cocoa programming with Python)

February 12, 2006

This is Apple’s tutorial about programming Cocoa with Python. It’s an easy one, but read it if you want to start programming Cocoa with Python. It is quite well done.

Here PyObjC website.


List user defaults in MacOS X

February 5, 2006

The small script below uses PyObjC (it was born as a snippet in ipython to have a quick check to a pair of variables). Of course you can write the very same thing in ObjectiveC.

I strongly encourage to install PyObjC and ipython even if you do work with Cocoa and ObjectiveC, since you can use that to prototype your application and to test snippets of code.


#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import Foundation

ud = Foundation.NSUserDefaults.standardUserDefaults()
d = ud.dictionaryRepresentation()

for k in d:
    sys.stdout.write(k)
    sys.stdout.write(":\t")
    try:
        print d[k]
    except UnicodeEncodeError, e:
        print d[k].encode('utf-8')
        
    

To have more information on PyObjC (that is to say Cocoa bindings for Python), go here.

You can find ipythonhere. ipython in an improved interactive shell, with powerful introspection capabilities.
Since MacOS X Python by default comes with no support for readline, I advise to install the missing module for Python 2.3 (search google, I don’t remember right now where to find it) or better install this version of Python 2.4, complete with the patch (on the same page).
Of course you must set your path so that the “first” python is the new one (if you set PYTHON_ROOT and such, you must also fix them).

Remember when you “python setup.py install” a module (that is the typical command line to install a python package), it is installed for the python version called by default (find it out with which pyhon)


Execute class: $(command) in python

February 3, 2006

Although you can easily do with module subprocess what I do with the following class:

  • If you have Python 2.3 you may not have subprocess
  • This class is much simpler than subprocess: it does just one thing.
  • You may want to pipe two commands in a fancier way

I wrote this because I needed to use Python to do some system automation and I needed something that acted like Perl and bash `` (in bash 2 you are supposed to do $(command) instead of `command` but it’s syntactical sugar).

In my strive to be compatible with standard MacOS system (which du use 2.3 by default) I forgot it existed module subprocess. So I wrote this simple class.

Even if in production you may prefer to use subprocess you can consider this an example of streams in Python. This is a very simple example. If you want to really pipe two processes, use module subprocess. If you want to make streams in a C++ fashion you can take inspiration by this code, but you may want to do it line based (but this ain’t no problem, in fact we are using properties, so you can do whatever you want with them — an example later).

Stream acts like a Mixin (thanks Dialtone!). It defines __rshift__ and __lshift__ (that become >> and


class Stream(object):
    def __rshift__(self, rho):
        if isinstance(rho, Stream):
            rho.input = self.out
            return rho
        else: 
            raise TypeError()

    def __lshift__(self, rho):
        if isinstance(rho, Stream):
            self.input = rho.out
            return self
        else: raise TypeError()
    

Remember: classes that subclass Stream must have an attribute named out and one named input. If you need something more complex, use properties to implicitly call functions every-time you access out or in. For example since I use lazy evaluation I do:


    input = property(fset=set_input, fget=get_input)
    out = property(fget=get_out)
    err = property(fget=get_err)

Now have a look at the whole code:


import os

class Stream(object):
    def __rshift__(self, rho):
        if isinstance(rho, Stream):
            rho.input = self.out
            return rho
        else: 
            raise TypeError()

    def __lshift__(self, rho):
        if isinstance(rho, Stream):
            self.input = rho.out
            return self
        else: raise TypeError()

class Execute(Stream):
    def __init__(self, command, *kargs, **prefs):
        self._commandString = ' '.join((command,) + kargs)
        self.has_run = False

    def _lazy_run(self):
        if self.has_run: return
        self.has_run = True
        (self.sin, self.sout, self.serr) = os.popen3(self._commandString)
        self.sin.write(self.input)
        self.sin.close()        
        self.out_list = list([line.strip() for line in self.sout])
        self.err_list = list([line.strip() for line in self.serr])

    def run(self):
        self._lazy_run()
        return self

    def _make_string(self, which):
        self._lazy_run()
        if hasattr(self, "%s_string" % which):
            return
        setattr(self, "%s_string" % which, 
                    '\n'.join(getattr(self, "%s_list"% which)))

    def set_input(self, s):
        if hasattr(self, "input"):
            self.input_ = "%s%s" % (self.input, str(s))
        else:
            self.input_ = str(s)

    def get_input(self):
        try:
            return self.input_
        except AttributeError:
            return ""

    def get_out(self):
        self._make_string("out")
        return self.out_string

    def get_err(self):
        self._make_string("err")
        return self.err_string

    input = property(fset=set_input, fget=get_input)
    out = property(fget=get_out)
    err = property(fget=get_err)

    def __str__(self):
        return self.out

print Execute("cat Execute.py") >> Execute("wc -l")
print Execute("wc -l") << Execute("cat Execute.py") 

If you need to do different things, redefine (get|set)_(out|input|err).


GeekTool CPU Script

January 28, 2006

If you don’t know what GeekTool is go here.
It is very likely you’ll find it useful. If you use Tiger, use this version that fixes a lot of issues.

GeekTool allows you to put a lot of interesting informations on the desktop. You can “print” logfiles on the desktop or you can put there pictures or, and that is what is interesting, put the output of a chosen command.
For example I put a random fortune on the desktop. It easier to do download GeekTool and do it than reading an explanation.

An interesting feature that is in the documentation (so it’s something you probably wouldn’t read) is that scripts/commands placed in “~/Library/Application Support/GeekTool Scripts” need not to be specified with full path. So we will put the script “cpuload.py” in that directory and we will refer to it with “cpuload.py”

And now the script:


    #!/usr/bin/python
    # -*- coding: utf-8 -*-

    import os

    class command(object):
        def __init__(self, c):
            self._command = c
            fi, foe = os.popen4(self._command)
            fi.close()
            self.out = list([line.strip() for line in foe])
            foe.close() 

    def cpu_load(string_list):
        # a bit functional... not pythonic at all
        return sum(map(lambda l: float(l.split()[2]), 
            string_list), 0)

    def main():
        ulist = command("ps -ux").out[1:]
        slist = command("ps -aux").out[1:]
        print "System CPU: ", cpu_load(slist), "%"
        print "User CPU:   ", cpu_load(ulist), "%"

    if __name__ == "__main__":
        main()

Class “command” is a stripped down version of a class I’m writing for another project. I also think that any script you write should be executable.


Symlink droplet

November 17, 2005

Although this can be easily adapted to work without MacPython’s
EasyDialogs, it would not make sense.
This is meant to be an applet used to easily create unix symlinks from
the Finder.

import os
import sys

import EasyDialogs
from EasyDialogs import Message

exec_path = sys.argv[0]
if len(sys.argv) < 2:
    Message('Drag a file onto SymlinkDroplet')
else:
    i = exec_path.find("SymlinkDroplet")
    dir_path = exec_path[:i]
    for file_ in sys.argv[1:]:
       if os.path.isfile(file_):
           linkname = os.path.basename(file_)
           os.symlink(file_, os.path.join(dir_path, linkname))


Parsing “lines”

November 16, 2005
00001: #define BUFFER_SIZE 1<<16
00002: #define ARR_SIZE 1<<16
00003: 
00004: void parse_args(char *buffer, char** args, 
00005:                 size_t args_size, size_t *nargs)
00006: {
00007:     char *buf_args[args_size]; /* You need C99 */
00008:     char **cp;
00009:     char *wbuf;
00010:     size_t i, j;
00011:     
00012:     wbuf=buffer;
00013:     buf_args[0]=buffer; 
00014:     args[0] =buffer;
00015:     
00016:     for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
00017:         if ((*cp != '') && (++cp >= &buf_args[args_size]))
00018:             break;
00019:     }
00020:     
00021:     for (j=i=0; buf_args[i]!=NULL; i++){
00022:         if(strlen(buf_args[i])>0)
00023:             args[j++]=buf_args[i];
00024:     }
00025:     
00026:     *nargs=j;
00027:     args[j]=NULL;
00028: }

To understand how it works, I suggest to take a look at the man page of strsep. As you can see this is a lot of code to perform a very simple task.
Compare with

        argv = cmd.split()

The C++ version is a bit longer (because I split some lines) than the C version, but there is less “magic”. In fact if you read the code it is very easy to understand how it works. Moreovere there is no hassle with memory allocation.

00001: #include <iostream>
00002: #include <string>
00003: #include <vector>
00004: #include <algorithm>
00005: #include <iterator>
00006: #include <cstdlib>
00007: #include <cerrno>
00008: #include <exception>
00009: #include <unistd.h>
00010: 
00011: using std::string;
00012: 
00013: void 
00014: split_string(const std::string s, 
00015:              const std::string sep, 
00016:              std::vector<std::string> &components)
00017: {
00018:     typedef std::pair<size_t, size_t> Range;
00019:     size_t pos, old_pos;
00020:     Range trange;
00021:     std::vector<Range> ranges;
00022:     trange.first = 0;
00023:     trange.second = s.find(sep, trange.first);
00024:     if (trange.second == string::npos){
00025:         trange.second = s.length();
00026:     }        
00027:     ranges.push_back(trange);
00028:     while(trange.second < s.length()){
00029:         trange.first = trange.second + 1;
00030:         trange.second = s.find(sep, trange.first);
00031:         if (trange.second == string::npos){
00032:             trange.second = s.length();
00033:         }
00034:         ranges.push_back(trange);
00035:     }
00036:     
00037:     for (int i = 0; i < ranges.size() ; ++i){
00038:        components.push_back(s.substr(ranges[i].first, 
00039:                 ranges[i].second - ranges[i].first) );
00040:     }
00041: }
00042: 
00043: class InvalidCommandLine : public std::exception {
00044: 
00045: };

Unixify

November 16, 2005

This is a small script to convert text files from old MacOS CR based format to MacOS X Unix LS based format

This is a very simple Python script I use to convert text files with Mac line endings in text files with unix line endings. It needs some Python libraries available only on MacOS, but it should not be difficult to make a portable version (without GUI or with another GUI toolkit).
This one is meant to be packaged as an applet with BuildApplet, included in MacPython.


# Enrico Franchi 2004(c)
# unixify.py
# Version 0.5
# This is released under Python Software Foundation Licence Version 2

# Usage: pythonw unixify.py
# You may build an applet.

import re
import os
import sys
import MacOS
import EasyDialogs

extensions= [".txt", ".text", ".mac", ".unix", ".dos", 
        ".c", ".cc", ".cpp", ".cxx", ".hxx", ".h", ".hpp",
        ".htm", ".html", ".xml", ".xsl", ".sgml", ".tex"
        ".py", ".pl", ".cgi", ".m", ".mm", ".texinfo" ]

def convert(file):
    """Creates a backup copy and converts \
    fileending from everything to unix"""
    try:
        fh = open(file,"r")
    except IOError:
        EasyDialogs.Message("Could not open %s" % file)
        return
    str = fh.read()
    fh.close()

    if str.find("\r") != -1:
        oldfile = file + ".old"
        try:
            fho = open(oldfile, "w+")
            fho.write(str)
            fho.close()
            del str
        except IOError:
            EasyDialogs.Message\
                 ("Could not open %s for writing" % oldfile)
        try:
            fh = open(file,"rU")
            str = fh.read()
            fh.close()
        except IOError:
            EasyDialogs.Message("Could not open %s" % file)
            os.remove(oldfile)
            return
        try:
            fh = open(file,"w")
            fh.write(str)
            fh.close()
        except IOError:
            EasyDialogs.Message\
                 ("Could not open %s for writing" % file)
            try:
                os.remove(file)
            except:
                pass
            os.rename(oldfile, file)
            return

def loop(dir, extensions):
    """ this calls convert recursively on all text files """
    goodFileList = []
    fileList = [ os.path.normcase(f) for f in os.listdir(dir) \
                 if os.path.isfile(os.path.join(dir,f))]
    dirList = [ os.path.normcase(d) for d in os.listdir(dir) \
                if os.path.isdir(os.path.join(dir,d))]
    for f in fileList:
        # now we are evalutating wether 
        # the file is likely to be ascii
        (root, ext) = os.path.splitext(f)
        try:
            is_there = extensions.index(ext.lower())

            if is_there:
                goodFileList.append(f)
                continue
        except ValueError:
            pass
        # checks TYPE
        #(cr, ty) = MacOS.GetCreatorAndType(f)
        #if ty=="ttxt":
        #   goodFileList.append(f)
    if len(goodFileList) > 0:
        (path, name) = os.path.split(dir)
        print "\n%s: " % name
    for f in goodFileList:
        convert(f)
    for d in dirList:
        loop(os.path.join(dir,d), extensions)

if __name__ == "__main__":
    if len(sys.argv) == 1:
        args = EasyDialogs.GetArgv()
    else:
        args = sys.argv[1:]
    for file in args:
        if os.path.isfile(os.path.normcase(file)):
            convert(file)
        elif os.path.isdir(os.path.normcase(file)):
            if EasyDialogs.AskYesNoCancel\
                     ("Do you want to process all files\
                     inside the directory %s?" % file,
                    cancel=""):
                loop(file, extensions)


Python Virtual Cron

November 6, 2005

Problem

Suppose you do want perform a certain task once in a while, and suppose you don’t have cron nor nothing similar. Suppose for example your environment is a CGI script and that it keeps almost no memory.

Of course every-time you run the script you can check if you are in a specified time range, and in that case you do run your action. In my opinion there is another way to accomplish this task: you can run the action based on a probability. This grants that the action is run in average m times every n uses (where m/n is the probability), and quite easily you can define an action that is run every-time (with n==m).

This approach in my opinion is better suited for a script that is called quite often and with action that need to be performed relatively often too, so that defining fine grained time ranges in which to run the supplementary actions becomes tedious.

Think for example in some database cleaning and consolidating. It’s more “use-based” than time-based.

Singleton

I did not write this particular code, but I use it in my vcron class. It’s freely available on the web, so no problem putting it here. As far as I know the author is Michele Simionato.

class Singleton(type):
    def __init__(cls,name,bases,dic):
        super(Singleton,cls).__init__(name,bases,dic)
        cls.instance=None
    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance=super(Singleton,cls).__call__(*args,**kw)
        return cls.instance

Put it in a file named “singleton.py” in the Python path or in the same directory as vcron.py.

vcron

Put the following code in a file called vcron.py. The license is a BSD based one, so you can do with it what you want, provided you give credit. I think it would be correct to give credit to Michele, too. You may check if you must on ActiveState Python Cookbook website, where I found this code. I am giving credit.

#  Copyright (c) 2004, Enrico Franchi
#  All rights reserved.
#  
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#  1) Redistributions of source code must retain the above copyright
#  notice, this list of conditions and the following disclaimer.
#  2) Redistributions in binary form must reproduce the above copyright
#  notice, this list of conditions and the following disclaimer in the
#  documentation and/or other materials provided with the distribution.
#  3) Neither my name nor the names of its contributors may be used to
#  endorse or promote products derived from this software without specific
#  prior written permission.
#  
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
#  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
#  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
#  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


import singleton
from random import randint

class VirtualCron:
    """VirtualCron emulates cron.

    This is used in environments where processes are not persistent and do
    not want to have memory of what happened, still they need to perform some
    tasks once in a while.
    It implements the singleton pattern, just one instance is allowed.

    Always use named arguments. Order of parameters is not guaranteed to remain the same.
    """
    __metaclass__ = singleton.Singleton

    FUNCTION = 0
    PARAMS = 1
    CHANCE = 2
    ACTIVE = 3
    RUNONCE = 4

    def __init__(self):
        self._actions = {}
    def add_action(self, name, function, params={}, chance=1000, active=True, runonce=False):
        """Adds selected action to VirtualCron object.

name is the name you use to access a record.
function is the function that will be called
params is the dictionary **kwds that will be passed to the function.
So it will be called function(**kwds) will be called.
1/ chance is the chance the function will be executed.
active means that the function is active
If runonce is True, then after being executed the function will be deactivated.
        """
        self._actions[name]=[function, params, chance, active, runonce]
    def del_action(self, name):
        del self._actions[name]
    def set_params(self, name, chance=None, runonce=None):
        if chance is not None:
            self._actions[name][self.CHANCE] = chance
        if runonce is not None:
            self._actions[name][self.RUNONCE] = runonce
    def activate(self, name):
        self._actions[name][self.ACTIVE]=True
    def deactivate(self, name):
        self._actions[name][self.ACTIVE]=False
    def change_named_parameter(self, name, arg_name, newvalue):
        rule = self._actions[name]
        kwds = rule[self.PARAMS]
        kwds[arg_name] = newvalue
    def run(self):
        for name, rule in self._actions.items():
            function, kwds, chance, active, runonce = rule
            if active and randint(1, chance)==1:
                function(**kwds)
                if runonce==True:
                    self.deactivate(name)

# some tests

## def printFactory(arg):
##     def _():
##         print arg
##     return _

## def printFactory2(arg):
##     def _(string):
##         print arg, string
##     return _

## v = VirtualCron()
## v.add_action('one', printFactory("one"))
## v.add_action('two', printFactory2("two"), params={'string':'kowabunga'})
## v.add_action('freq', printFactory("frequentissima"), chance=100)
## v.add_action('unique', printFactory("=====UNICA!======"), runonce=True)

## for i in xrange(10000):
##     v.run()