Legume 0.2

Yup version 0.2 is available – obtain it via googlecode or pypi. I’ve made substantial changes to the API and the guts so it’s completely incompatible with version 0.1.

 

Legume – documentation

The API documentation for Legume 0.1 is now available, and will be updated regularly as more is added.

Version 0.2?

The work on the documentation and removing the bugs listed on the google-code page is towards the next release of Legume, which in all probability will be in the new-year. Stay tuned 🙂

 

Backing up a Subversion repository to Amazon’s S3

I’m currently working through the process of re-building my development server and have got to the stage of configuring the backup of my subversion repository to Amazon’s S3 storage ‘cloud’. Due to the inevitability of having to do it again in the near future I’m going to document how I keep my repository backed up, for myself, and for everyone else:

(This guide is assuming a freshly-squeezed Ubuntu 9.04 server install)

0. Obtain an AWS account if you haven’t already – making note of the Access key and Secret key, you’ll need them in step 2.

1. Install s3cmd

root@trogdor:~# sudo apt-get install s3cmd

2. Configure s3cmd and follow the onscreen instructions

root@trogdor:~# s3cmd --configure

3. Create the backup location

root@trogdor:~# mkdir /var/nightly_backup

4. Create the backup script (/var/nightly_backup/backup.sh)

#!/bin/bash
svnadmin dump --quiet /var/svn/repos | gzip > /var/nightly_backup/svn.gz
s3cmd sync /var/nightly_backup s3://backup_bucket/nightly_backup/

5. Make the backup script executable

root@trogdor:~# chmod +x /var/nightly_backup

6. Amend /etc /crontab to run the backup at a suitable time (3am in this case)

... contents of /etc /crontab ...
0 3 * * * root /var/nightly_backup/backup.sh

7. Wait until 3am, or run /var/nightly_backup/backup.sh.

8. Download an S3 browser such as S3 Browser (Windows) or Jets3t (Java)

9. Configure your S3 browser of choice to point to your AWS account to confim that the backup completed successfully.

10. ?

11. Profit.

 

legume 0.1 released: easy_install ‘legume’

It’s been a few months in the making, but now legume has gotten to a point that I thought it worthy of a 0.1 release. The basic feature-set is complete, so it’s in a usable state, that is if anybody can be be faced with using it yet, as at the moment there’s very little (read: nothing) in the way of documentation or user guides.

The release is available via pypi or google code, and I think that softpedia has already snapped it up.

As for the name: “It’s not a nut, it’s a legume! phew!” (Ricky Gervais, Animals).

 

Python distutils

A guide to creating a simplistic .exe python package installer:

  1. Create a setup.py file in the directory above your package directory:
    MyPythonPackage
    setup.py
    
  2. Copy the following in setup.py:
    from distutils.core import setup
    import setuptools
    import sys
    
    setup(name='MyAmazingLibrary',
        version='0.1',
        description='My Amazing Library',
        author='My Name',
        url='http://code.google.com/p/MyAmazingLibrary/',
        packages=setuptools.find_packages('.'),
        package_dir = {'':'.'},
    )
  3. Open a command prompt in the directory containing the setup.py and execute
    python setup.py bdist_wininst
    
  4. Observe how the installer appears in the newly created dist directory.

Additional Notes

  • To specify that the installer is only for a specific Python install use the –target-version option with setup.py, eg:
    python setup.py bdist_wininst --target-version=2.5
    
  • Swap bdist_wininst to bdist_msi to create a Windows Installer for your python package.
 

Event handling in python

Update: for the latest code please check nevent.py in the legume google code repository

Typically, event handling in python either involves storing a function pointer and invoking it at some pointer later-on, or inheriting a base-class and overriding event handler stubs. Storing a single function pointer does not permit event handler chaining, but is the easiest method to expand upon, and acts as a replacement for event handler stubs in classes.

Expected usage of the Event handler:

def example_handler(param):
    print "1", param

example_event= Event()
example_event += example_handler
example_event('Hello!')

And the output:

1 Hello!
2 Hello!

A basic structure to the event handler class is shown below:

class EventError(Exception): pass

class Event(object):
    def __init__(self):
        self._handlers = []

    def __iadd__(self, other):
        if other not in self._handlers:
            self._handlers.append(other)
        else:
            raise EventError, \
                   'Event %s error: Handler %s is already bound' \
                   % (self, other)
        return self

    def __isub__(self, other):
        try:
            self._handlers.remove(other)
        except IndexError, e:
            raise EventError, \
                   'Event %s error: Handler %s is not bound' \
                   % (self, other)
        return self

    def __call__(self, sender, args):
        result = None
        for handler in self._handlers:
            result = handler(sender, args)
        return result

    def is_handled_by(self, handler):
        return handler in self._handlers

Note how overriding __iadd__ provides a syntax identical to C# for adding extra handlers onto the event handler chain – marvelous.

This code is actually based off the event handling used in the legume network library, see
nevent.py in the legume google code repository for the code in use, and to see any further changes.

 

A unittest green bar for the console

One thing the unittest module is missing for me is a green bar of success (or a red bar of fail), so here’s one I cooked up to scratch my itch (windows only at the mo’, sorry):

import sys
import ctypes
import unittest

class GreenBarRunner(unittest.TextTestRunner):
    FOREGROUND_GREEN= 0x0A
    FOREGROUND_RED  = 0x0C
    FOREGROUND_WHITE= 0x0F
    FOREGROUND_YELLOW=0x0E

    def __init__(self, verbosity=2):
        unittest.TextTestRunner.__init__(self, verbosity=verbosity)
        STD_OUTPUT_HANDLE = -11
        self.std_out_handle = ctypes.windll.kernel32.GetStdHandle(
            STD_OUTPUT_HANDLE)

    def set_color(self, color):
        bool = ctypes.windll.kernel32.SetConsoleTextAttribute(
            self.std_out_handle, color)
        return bool

    def run(self, test):
        r = unittest.TextTestRunner.run(self, test)

        failed_count = len(r.failures)
        errored_count = len(r.errors)
        total = r.testsRun
        ok_count = total - (failed_count + errored_count)

        sys.stdout.write('\n[')
        self.set_color(self.FOREGROUND_RED)
        sys.stdout.write('#' * errored_count)
        self.set_color(self.FOREGROUND_YELLOW)
        sys.stdout.write('#' * failed_count)
        self.set_color(self.FOREGROUND_GREEN)
        sys.stdout.write('#' * ok_count)
        self.set_color(self.FOREGROUND_WHITE)
        sys.stdout.write(']\n')

Usage:

suite = unittest.TestLoader().loadTestsFromModule(example_module)
GreenBarRunner(verbosity=2).run(suite)

It’ll execute the TextTestRunner so you won’t lose the test names and stack traces and afterwards display a red/yellow/green bar:

GreenBarTestRunner

Ta-da.

 

Zombies!

Hey folks, yup first blog post here! (and hopefully not the last)

Two days ago I began work on a zombie shoot-em-up top-down game – basically you get chased down by unending hordes of zombies and you’ve got to shoot them all. At the moment it’s pretty basic, but I’m happy with the progress I’ve made so far:

The player currently moves around using WSAD for forward, backpedal and strafe, and uses the mouse to aim/face in a particular direction.

Specifics of stuff implemented:

  • Map scrolling
  • Zombies pathfind using A*, and adjust their route so every couple of seconds. They have no maximum range, and can track the player across the map. With more than two dozen zombies the game crawls to a halt (unless the player stands still).
  • Blood splats and spray appear with each shot. The splats will stay on the ground for a little while, and the spray will stick to walls.
  • Ammo counter and ammo crates.
  • Huge compensation cannon pulse rifle.