Elixir and Just Enough Magic
Via my usual Python dripfeed (PlanetPython) I read the announcement about Elixir.
Elixir is a new declarative mapper for SQLAlchemy. It draws inspiration from ActiveRecord (Ruby on Rails) and has what is both a very nice syntax, and an unusual one for Python :
has_field('name', Unicode(60))
has_many('movies', of_kind='Movie', inverse='director')
using_options(tablename='directors')
class Movie(Entity):
has_field('title', Unicode(60))
has_field('description', Unicode(512))
has_field('releasedate', DateTime)
belongs_to('director', of_kind='Director', inverse='movies')
has_and_belongs_to_many('actors', of_kind='Actor', inverse='movies')
using_options(tablename='movies')
class Actor(Entity):
has_field('name', Unicode(60))
has_and_belongs_to_many('movies', of_kind='Movie', inverse='actors')
using_options(tablename='actors')
So how are the classes here configured, when there are only what look like function calls within the class namespaces ?
A peek into the source code reveals the trick.
has_field (to pick an example) is defined in fields.py. They are classes, wrapped inside instances of the Statement class.
When they are called, the call is added to a list of statements, stored as a class attribute Statement.statements.
Entity has a metaclass, which means that when subclass definitions are executed (at the time the module is imported) then Statement.process is called.
Statement.process is a class method, which processes all the statements that have been added to the list (which is then cleared). Because the list is cleared each time process is called [1], statements only contains entries for the class currrently being created. It's not thread-safe, but then importing modules isn't anyway.
This is a very nice trick, not too difficult to work out, but just enough magic. I really like the syntax anyway. I'm not sure I'd use a trick like this myself, but the authors of Elixir have created a nice DSL. (With not a self in sight as Andrzej was quick to point out - oh and the source code is very readable, which is a good sign.)
| [1] | The metaclass is instantiated, to actually create the class, once the code in its namespace has been executed. |
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-13 20:52:33 | |
Automatic Properties and a Metaclass Conundrum
I've fixed the (fairly subtle) bug in my properties metaclass (see the previous blog entry).
This means that instead of using the metaclass directly, you can create a class that uses it, and just subclass that :
__metaclass__ = __properties__
Any subclasses of WithProperties will have properties automatically created from methods declared with a get_, set_ (etc) prefix [1] :
def __init__(self):
self.__value1 = "A read only value"
self.__value2 = None
def get_readonly(self):
print 'getting readonly attribute'
return self.__value1
def get_test(self):
print 'Getting test attribute'
return self.__value2
def set_test(self, value):
print 'Setting test attribute'
self.__value2 = value
This started out as a toy implementation, but I think this is actually a nice way of declaring properties. I might even use it...
The original version of the metaclass worked fine, but when you subclassed a class using it, the metaclass wouldn't be called for the subclass. The subclass still had its __metaclass__ attribute set, it just wasn't being used.
The bug was due to the final line of the __new__ method in my metaclass (I've already changed it in the post below).
The offending line was :
Can you spot what is wrong with this?
It took Christian [2] and me several whole minutes of forehead-wrinkling scrutiny (and a close encounter with some code by Ian Bicking) to work it out.
All classes are types. Metaclasses creates new instances of types, with the specific characteristics that you supply. When a class is created (normally using the class statement), the metaclass is called, which is responsible for creating the new class.
Creating a new class using type(classname, bases, newClassDict) meant that my class was an instance of type - not of my metaclass.
The reason that subclasses still had the __metaclass__ attribute set was because of the normal inheritance rules...
Changing that line to type.__new__(meta, classname, bases, newClassDict) means that the my class really is an instance of the metaclass, and subclassing it works as expected.
...
>>> type(x)
<type 'type'>
>>> class y(type): pass
...
>>> class x(object):
... __metaclass__ = y
...
>>> type(x)
<class '__main__.y'>
I've updated my article on metaclasses to include this.
Oh, my achievement on the book on Saturday was writing minus seven pages of the Python tutorial. Refactoring at work.
| [1] | The getter and setter methods don't appear in the class namespace. |
| [2] | Xtian has finally posted a new blog entry after months. This is great news as he is one of the most profoundly ridiculous people I know. |
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-12 21:18:24 | |
Painless Properties
Python properties are great. They can provide a clean API with useful functionality.
def __init__(self):
self.__attribute = None
def __getattribute(self):
return self.__attribute
def __setattribute(self, value):
self.__attribute = value
def __delattribute(self):
print "You can't delete attribute."
attribute = property(
fget = __getattribute,
fset = __setattribute,
fdel = __delattribute,
doc = "Some docstring"
)
There are two problems with properties. The syntax is a bit ugly and it leaves your getter and setter methods inside the class namespace.
The hack below is a metaclass which offers one solution to these problems. You don't use property directly, but declare methods that begin with 'get_', 'set_' or 'del_'. You can also have class attribute docstrings that start with 'doc_'. Following 'get_', 'set_', 'del_' or 'doc_' is the property name they belong to.
A class that uses this metaclass will have the appropriate properties created, and the methods used won't appear in the class namespace.
def __new__(meta, classname, bases, classDict):
names = set()
propertyDict = {
'doc': {},
'get': {},
'set': {},
'del': {}
}
newClassDict = {}
importantNames = set(['del_', 'doc_', 'get_', 'set_'])
for name, item in classDict.items():
if name[:4] in importantNames and len(name) > 4:
propertyName = name[4:]
names.add(propertyName)
propertyDict[name[:3]][propertyName] = item
else:
newClassDict[name] = item
for name in names:
fget = propertyDict['get'].get(name)
fset = propertyDict['set'].get(name)
fdel = propertyDict['del'].get(name)
doc = propertyDict['doc'].get(name)
newClassDict[name] = property(fget=fget, fset=fset,
fdel=fdel, doc=doc)
return type.__new__(meta, classname, bases, newClassDict)
class WithProperties(object):
__metaclass__ = __properties__
To see it in action, create an instance of the following class and access the 'test' and 'readonly' properties. You can also look inside the class namespace using dir(Test) to verify that the getter / setter / etc methods and attributes aren't there. You should also see that type(Test) is __properties__.
def __init__(self):
self.__test = 3
self.__readonly = 2
def get_test(self):
print 'Getting test'
return self.__test
def set_test(self, value):
print 'Setting test'
self.__test = value
def del_test(self):
print 'Attempting to delete test'
doc_test = "Docstring for test property"
doc_readonly = "a read only property"
def get_readonly(self):
print 'Getting readonly'
return self.__readonly
Obviously this is only a toy implementation, caveat emptor.
The only issue that I'm aware of (which is easy to fix if you have the desire) is that it uses sets, so it requires Python 2.4 or greater.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-11 18:55:43 | |
Microsoft Invented AJAX (and they wish they hadn't)
I've just read a very interesting blog entry by Jeff Attwood: Did IE6 Make Web 2.0 Possible?.
By somewhere around 2002 Microsoft had effectively won the browser war. IE 6 was introduced in August 2001. Up until then Microsoft had released a new version of IE every year or eighteen months or so.
XMLHttpRequest, which is central to AJAX, was introduced in IE 5.0 in March 1999 as a proprietary feature.
Conventional wisdom says that Microsoft stopped developing the browser after they won the war because they were scared of the 'web as platform' damaging the market for desktop applications.
Jeff suggests, rather ironically, that by letting the browser market stagnate (so that by 2004 something like 95% of people browsing the internet were using a single browser version) Microsoft made it dramatically easier for people to contemplate writing web applications!
The super-saturation and monoculture of IE6 from 2002 to 2004 created an incredibly rich, vibrant development platform where developers were free to push the capabilities of the browser to its limits. Without worrying about backward compatibility. Without writing thousands of if..else statements to accommodate a half-dozen alternative browsers.
Nice theory.
Personally I think the web still sucks as a platform (anyone using a browser based IDE yet ?) and I don't see much sign of that changing. Nicer web apps are great, but why does everything need to be delivered through a browser ? I think client apps with collaborative features (or other web service integration) are the way to go.
Oh, another interesting thing:
This is also from last year. Slides 36-38 show exactly which features from Python Javascript 2 will borrow. This is iterators, generators and list comprehensions.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-11 14:45:49 | |
Categories: General Programming
Happy Birthday Voidspace
Voidspace.org.uk is four years old today.
Happy birthday it.
In web years that's practically ancient...
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-11 00:58:58 | |
Categories: Website
Cats or Dogs, C# or Java
I like James Tauber's new site, Cats or Dogs [1].
It asks you a series of questions, where you choose one thing over another. Then it presents you with some information about other people's choices.
For example :
People who prefer c# to java are 3.0 times more likely to prefer boxers to briefs. People who prefer java to c# are 50% more likely to prefer briefs to boxers. This might be significant.
You can view some of the results it generates, here. Can you guess that I'm trying to find a distraction from writing ?
| [1] | I presume it is written with Django. |
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-10 23:12:49 | |
Categories: Fun, General Programming
And Today in the Junk Folder
Oops... it looks like someone forgot to fill in a few values before firing up their evil spam machine :
%TO_CC_DEFAULT_HANDLER Subject: %SUBJECT Sender: "%FROM_NAME" <%FROM_EMAIL> Mime-Version: 1.0 Content-Type: text/html Date: %CURRENT_DATE_TIME %MESSAGE_BODY
Received today. My current favourite is the subject of a piece of spam I received yesterday: The Chronicles of The Rogue Pirate Ninjas: Revised, Act One.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-10 12:05:55 | |
Categories: Computers
How Vista is Good for Python (well... maybe)
Smart and perceptive people that you are, I'm sure that the recent series of 'Mac vs PC' adverts won't have escaped your notice. Windows Vista is finally out, and for the first time in living memory [1] computer users everywhere are considering a major change to their operating system.
Apple is hoping that some of those people will switch to Mac OS. And why shouldn't they?
Apple's star has been rising of late. The iPod underpinned their financial stability, cheaper hardware have contributed to rising sales and the buzz about Vista has led to mainstream press [2] coverage about how Vista is just Microsoft playing catch-up with Mac OS. The capability of running Windows on Mac hardware is also helping those on the edge of changing to make the jump.
Linux users can take heart from this. A lot of work has gone into making Linux easier to install, and friendlier for non-ultra-geeks to use on the desktop. Distributions like Ubuntu are making this their explicit goal. Some Mac hardware owners run Linux, and if running a non-Microsoft OS on the desktop is becoming fashionable then it can only be good news for Linux.
None of this changes the fact that Microsoft are starting from a pretty good position. Statistically almost every desktop computer in the world is running their operating system. I can't be bothered to look it up, but what is it - still greater than 95% ?
Assuming that more people continue to switch away from Windows, and it certainly looks that way, then eventually we will get close to a magical tipping point.
At the moment it makes financial sense for many software companies, particularly small ones, to develop exclusively for the Windows operating system. It simply isn't worth the investment of time and resources to develop for alternative platforms. At some point it will not only be a nice idea for software firms to make their applications cross-platform, but it will become good business sense - followed not long after by essential business sense. Where is that tipping point ? 15% ? 20% ?
As we approach this point programming languages with a strong history of providing hassle-free cross platform development environments will be ever more important. That includes Python.
Not only that, but cross-platform libraries like wxWidgets and QT will also become more important. This is good news for users of those libraries, whichever language they are consumed from.
| [1] | Possibly a minor exaggeration. |
| [2] | I mean real world press, not just the stuff us geeks read. |
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2007-02-10 01:17:54 | |
For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store. If you're looking for a new techie job, try the Voidspace Tech Job Board. This is part of the Hidden Network of technology and programming jobs.

IronPython in Action


