Oxfam America banner

Friday, August 29, 2008

Python's vars()

The builtin function vars() in Python will give you the argument's __dict__ or the namespace of the object. That is the namespace of the object not the object and the names belonging to its class - an important distinction. In short, it is simple a syntactic nicety over referencing the property __dict__ directly. For example, contrast the following:


>>> namespace = vars(o)


With:


>>> names = o.__dict__


But for introspecting objects to produce some custom serialized form (json comes to mind), this is likely not what you want to do. This post will attempt to explain why not.

Say we want to explicitly convert an object to some more primitive form (a dictionary or list) and we use vars() to introspect the attributes of the object. Maybe our serialization function will also account for things we don't want to serialize - like things we don't want to expose over the network for security or bandwidth reasons. Here's a simple example:


class Thing(object):
def __init__(self, a, b, secret=None)
self.a = a
self.b = b
self.secret = secret
self._fd = open('file.txt')

def dictifyThing(thing):
blacklisted = ['secret', '_fd']
data = dict(vars(thing))
for bl in blacklisted:
del data[bl]
return data


That will work fine for this example. And so we're happy:


>>> thing = Thing( 1, 2, "don't tell anyone" )
>>> dictifyThing(thing)
{'a': 1, 'b': 2}


A great (though not unique) feature of Python are properties which allows us to make derived read-only attributes on an object or attributes that carry side-effects upon setting. For example, we might have an extension of Thing where a is read-only and derived from the value of b:


AnotherThing(Thing):

def __init__(self, b, secret=None):
self.b = b
self._fd = open('data.txt')

@property
def a(self):
return self.b - 1


However, dictifyThing() will no longer work the way we want it to:


>>> thing = AnotherThing(2, "open sesame")
>>> dictifyThing(thing)
{ 'b': 2 }


One should expect this behavior, because a, being a derived property, isn't actually part of the namespace of an instance of AnotherThing. This conversely illustrates another advantage of properties: calculating the value of a and setting as a normal attribute is undesirable both for space efficiency reasons and the fact that we wouldn't have a definite way of constraining the numerical relationship between a and b.

Does this mean we can't write a decent version dictifyThing()? No, probably the more commonly known dir() will give us all the referenceable names of an object - which then must include class attributes. We can use this to write something nicer:


def dictifyThing(thing):
blacklisted = ['secret']
data = {}
for name in ( n for n in dir(thing) if n[0] != '_' and
n not in blacklisted ):
data[name] = getattr(thing, name)
return name


Now our encoding scheme works nicely for both Thing and AnotherThing. A small improvement would be to use a higher-level function make defining new encoders simpler:


def _encode(include=(), exclude=()):
def f(o):
data = {}
if include:
for name in include:
data[name] = getattr(o, name)
return data
for name in ( n for n in dir(thing) if n[0] != '_' and
n not in exclude ):
data[name] = getattr(thing, name)
return data
return f

encodeThing = _encode(exclude=('secret'))
encodeFoo = _encode(include=('a','b','c'))

Thursday, August 21, 2008

I Smell Fear

I though it would be amusing to do a redacted blogpost. After all, the assholes with power do this stuff all the time and for things that really matter - life or death matters ... seriously. The stupid blog post is the exact antithesis - things of relatively no matter whatsoever. Were it to provoke one, such a reaction would be quite absurd. So here it is - a repost of an internal blogpost from work replete with inked out sections (or "so much whiteout") like xxxx xxxx. I hope the all-seeing corporate eye of Sauron looming down on the piteous ant I am - above my head, my cube our ceiling and piercing through the clouded firmament shading all humble living beings on this green earth - looks kindly upon my ramblings here.



[Begin repost]







xxxxx xx xxxxxxxxxx xxx xxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxx, here's a little splash on the fire from my whiskey flask:



http://antoniocangiano.com/2008/03/04/rails-is-the-best-thing-that-ever-happened-to-python/

... Some great humor relating to differing attitudes towards marketing (that's the `sexy', in case you're wondering) between the Ruby and Python communities. My favorite quote from the above:




If Twisted Matrix was implemented in Ruby it would be advertised as the second coming ...



Anyhow, read on, because he's not really bashing Rails:




So what does this mean for me personally? I’ll use them both, as I’m a firm believer in using the right tool for the right job.


The last point is the single most important: Use the Right Tool for the Right Job. For the last few years (lets call them the Dark Ages now) in Enterprisey-Land, the motto has been One F***ing Tool for Every F***ing Job. The are also variations on the motto that end in "... Or Else!" x xxxx xxxx xxxxxx xxxxx xxxxxx xxxxx xx xxxxxx xx xxxxx xxxxxxxx xx xxxxxx xxx xxxx xxxx xxxxxx xxxx xxxxxx xxx xxxx x xxxxxx xxxxxxxx xxxx xxxxxxxxxxxxxx xxxx xx xxxxx xx xx xxxxxxx xxxxx xxxxx xx xxxxx xxxxxxxxx xx xxxxx xxxxxxx xxxx xx x xxxxxxxx xx xx xxx xxxxxxxxxxxxx xxxxxx xxxxx xxxx x xxxxxxxxxx xxxxxxxxxx xxxx xxxx xxxx xxxx xxx x xxx xx xxxxx xxx xxxxx xxx x xxxxxxx xxxxxxx xx xxxxxxx xx xx xxxx xx xx xxx xxxxxxxxx xx xxx xxxxx xx xxxxxxx xxx xxx xxx xxx xxx [1]x

Of course I don't have the statistics to back this (who does?), buy my perception (which is arguably valid considering I work day and night as a Java developer and mingle with other prisoners of the `Enterprise'), homebrew Java projects outside the scope of well-understood [2] frameworks (Spring MVC, Webwork) almost always necessitate a great amount of design decisions to be made up front. To quote Glyph Lefkowitz xxxxx xxx x xxxxxxx in a recent discussion on twisted python:


A design discussion is an unverified hypothesis. There's no point in developing it into a theory until you have some further indication that it might be implemented.


The Spring team realized this and began a successful political campaign [3] against all things J2EE, and focused on implementations, instead of bullsh**, I-Smell-Fear, specifications. (Notice, how politically correct I am in censoring my own writings. I wish I could put asterisks in my speech as well.)

Design decisions are made up front with most Java projects. However, at a higher level, that a matter of individual programmer/architect or wider spanning community philosophy. The same philosophy could be adopted by Pythonistas, Rubyists or Lispers (and it unfortunately is by some people who haven't read this), and you would end up with similar issues. Note that I use the word `similar', not `same', because the issue is compounded in Java by the instilling of the up-front design into the lower-level implementation. The instilling process isn't optional - it's demanded by static typing. Static typing is not (always) a bad thing, and the rigid stance taken by static type puritans who love languages like Java and Haskell makes some sense when considering the legacy of weakly typed languages like C. But that's a whole another story - but briefly, let's remind ourselves that Python and Ruby are strongly (not weakly) typed, but also dynamically typed which essentially enables you to punch an API in the face (with 2-5 lines of readable code) if you don't like it, or even modify is ill behavior at runtime [4] if you can't convince the stubborn maintainer to fix bugs or funny smells.

Now, one meme that remains in the sinking ship of Enterprise Snake-Oil [5] is that all the conveniences provided by dynamic languages are a wash because the end result is a bundle of cute little scripts [6] which suffer in performance and are hacked out and difficult to maintain. Let me go ahead and call bullsh** on that. x xxxx xxxxx xx xxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxx xxx xxxxxxxxxxxxx xx xxx xxxxxxx xxxxxxxxx xxx xxxxx xxx xxxxxx xx xxxxxx xxxx xxxxxxxxxxxxxxx xxxxx xx xxxxxxx xxxxxxxxx xx xxxxxxxx xxxxx xx xxxxxxxxxxxx xxxxxxxxxxx xx xxx xxxxxxxx xxxxxxxxxxx xxxxxxxx xxxx xx xxxxxxxx Java combined with our fancy-schmancy IDE and all their attempts to slap developers on the wrist while they pound away on source code have done very little to keep us from sinning. The only solutions to the problems that arise in bad Java programming are enforcing design patterns we're all slow to adopt: beware of the singleton, write components instead of final service classes with static methods (so you can actually write unit tests around things), use Spring (or replace Java with XML), etc. In sum, understand all the esoteric details of the Java programming language (event the transient modifier, if serialization is important to you) while laying out classes and deciding on method signatures - again, these will be irreversible decisions that will lead to hate from your fellow developers who can't pragmatically work around your anti-patterns to facilitate better unit-testing, adaptive integration points, proper serialization, and other goals easily attained with dynamic languages.

Another meme is that Java (or more generally, static typing) facilitates quality (and security) through type safety. On the security front, this is true in some extreme cases - though it's a problem easily overcome in distributed computing by people with smarts. Quality ensured through compilation is of course a joke - compilers don't know the requirements, they only speak 1s and 0s. Ultimately, the larger task is preventing regressions, which can be done only with unit tests. And there are plenty of Python projects that get this very right. Take the unit test suite for the Twisted project as an example:



Ran 4113 tests in 172.023s

PASSED (skips=62, expectedFailures=19, successes=4032)


Yeah, just a few tests there. And yes, that's 172 seconds (not minutes). I guess there are some equivalents in open source Ruby projects. Ruby people?



Ok, so my little blog entry is supposed to be about how the next alternatives to Java are apparently Ruby and Python and my own personal fear is that Ruby will be just the next Java - and people will claim I'm not sexy enough with my old bag-o-tricks in Python [7] .. blah, blah, even though I know in my heart of hearts that "Python is the only acceptable implementation of Ruby". Really, it takes more energy than I'm willing to expend to get into these silly arguments. xx xx xxxx xxxxxxx xx xxx xxxxxxxxxxxxx xxx xxxx xxxxxxxx xxx xxx xxx xx x xxxx x xxxxx x xxx xxxxxxxxxx xxxx xxxxxxxx xxxx xxxxxxxxxx xxxxxxxxx xxxxxxx xxx xxxxxxxxxx xxxx x xxx xxxxxxxxxx xxxx xxxx xxxxx xxx xxxx xxxx xx xxxxxx xxxxxxxxxx xx xxxxxxxxx xxx xx xx xxxxxx xxx xxx xxxx xxxx xxxx xxxx xxx xxxxxxxxx xxx xxx xxx xxxxxxxxxx xx xxxxx xxxxxxxxx xxxxxxxx xxx xxxxxxx xxx xxxxxxxxxxxx xx xxxxx xxxxx xx xxxxxx xxxx xxxx xxxx xxx x xxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxx xxx xxxxx xxxxxxxx xxxx at least one painful piledriver.






[1] EST is an acronym for Enterprise Service Toilet.

[2] Well, let's just wave our hands and pretend like they're really well understood.

[3] There were some problems here as well. It appeared the platform they were running on was build of some space-age cardboard, but on closer inspection, it ended be highly compressed XML - about 500K lines of it. Oh ... And they basically reimplemented J2EE all over again - since that stuff is so tasty, right? The best approach, in my jaded world-view, would have been to slit the Hegemon's throat, instead of knocking it's head with a bat and then proceed to built a neck brace for it.


[4] In Python, this is called monkey-patching (which involves trivial reassignment of unbound methods, functions in a module's namespace, etc), and Ruby facilitates `stepping into' classes. Java offers some incomprehensible bullsh** on this front, which will only necessitate 3 months of training before any developer can partially utilize it - of course it will likely continue to feel like acupuncture in your eyeballs when you revisit code leveraging such awe-inspiring byte-code futzing libraries.

[5] Ahh, rhetoric ... gotta love it.


[6] Yes, you can write applications with modules and namespaces. Isn't that nifty?

[7] This is yet another meme affecting at least part of the Ruby community - Python is some old crusty crap no-one hacks with except dorky research scientists who wear their pants up too high. This is of course nonsense - and to paraphrase Zed Shaw: "Ruby will not lend you more appeal to the ladies." Of course, Zed used his own branded terminology to convey the equivalent concept.

Monday, August 11, 2008

Devil in the Shadows

I've taken the first steps towards implementing shadows for miru -- glorified 3d extensions (and a handful of arbitrary features) for pyglet. I had avoided implementing a shadowing effect for miru for some time, for fear that my brain wouldn't be able to handle the math, or I'd get lost in the gnarlier details of the OpenGL API and just throw my hands up after hours of time trying to get it right. Last night I was able to finally hammer out a proof of concept which still needs a lot more work (and maybe even more underlying library support) before I can build something decently generalized.



The technique I used to generate the shadows is referred to as shadow mapping - which is simply in theory but gets somewhat complicated in the implementation - in particular when you consider limitations of texture sizes for many common OpenGL drivers and other potential texture artifacts in a scene. My own implementation was borrowed mostly from an examples in the excellent OpenGL(R) Super Bible--I guess I should return this to my company's library someday since I've been hogging it for several months now. Shadow mapping entails creating a depth texture which means copying the z-buffer from the perspective of a light looking down onto the scene into a texture region to be applied to objects in the scene using eye-linear texture coordinates.

(It should be noted here that the process I use is a somewhat old school way of shadow mapping, though not as old-school and crude as stencil testing. With modern graphics hardware, vertex shaders and per-pixel lighting can be used to achieve much nicer and softer shadows and their penumbra. My goal is to first implement something using a traditional multipass technique and then try something more sophisticated with shaders--again, if my brain can deal with the math.)

To illustrate the first step in the shadow mapping process, the following code (which assumes there is single globally bound texture object) could be run at start of a program and whenever the light moves in the scene:


scene_radius = 95.0
light_to_scene_distance = math.sqrt(
light_pos[0]**2 + light_pos[1]**2 + light_pos[2]**2)
near_plane = light_to_scene_distance - scene_radius
field_of_view = miru.math3d.radians_to_degrees(2.0 *
math.atan(scene_radius / float(light_to_scene_distance)))

tmp = ( GLfloat * 16)()

glMatrixMode( GL_PROJECTION )
glLoadIdentity()
gluPerspective( field_of_view, 1.0, near_plane,
near_plane + (2.0 * scene_radius) )
glGetFloatv( GL_PROJECTION_MATRIX, tmp )
light_projection = euclid.Matrix4()
light_projection[:] = tmp[:]

# Move to light's perspective
glMatrixMode( GL_MODELVIEW )
glLoadIdentity()
gluLookAt( light.pos.x, light.pos.y, light.pos.z,
0.0, 0.0, 0.0, 0.0, 1.0, 0.0 )
glGetFloatv( GL_MODELVIEW_MATRIX, tmp )
light_mview = euclid.Matrix4()
light_mview[:] = tmp[:]

# Clear the depth buffer only
glClear( GL_DEPTH_BUFFER_BIT )

# Remember the current shade model and setup driver state
...

# do a render pass
render()

# Copy depth values into depth texture
glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
0, 0, shadow_width, shadow_height, 0 )

# Restore prior driver state
...

# Setup up the texture matrix which will be use in eye-linear
# texture mapping
tex_matrix = euclid.Matrix4()
tex_matrix.translate(0.5, 0.5, 0.5).scale(0.5, 0.5, 0.5)
tex_matrix = (tex_matrix * light_projection) * light_mview
tex_matrix.transpose()
# Give us immediate access to ctypes arrays
tex_matrix = (
(GLfloat * 4)(*self.tex_matrix[0:4]),
(GLfloat * 4)(*self.tex_matrix[4:8]),
(GLfloat * 4)(*self.tex_matrix[8:12]),
(GLfloat * 4)(*self.tex_matrix[12:16])
)


In the above I omitted some details such as setting up and tearing down the required driver state. (I'll post a link to the completed source code at a later time.) The import parts are all intact, though, which include copying over the depth buffer to the current bound texture:


glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
0, 0, shadow_width, shadow_height, 0 )


It is important to note that this is done after rendering the scene, of course, and setting the view matrix according to the light's perspective. This are just simple API details, but the math comes at the end (saving the best for last):


tex_matrix = euclid.Matrix4()
tex_matrix.translate(0.5, 0.5, 0.5).scale(0.5, 0.5, 0.5)
tex_matrix = (tex_matrix * light_projection) * light_mview
tex_matrix.transpose()
... translate into ctypes array


All the matrix operations are done using pyeuclid which is a pure-python library well-suited for graphics programming being focused on vector, matrix and quaternion operations; however, I've submitted a patch to add transpose() methods which are not currently provided. In first two lines we translate and scale an initial identity matrix by 0.5 along each axis to create a bias which allows us to translate the actual coordinates captured in the range (-1,1) to the range (0,1)--normalized device coordinates, or screen coordinates. We multiple the bias matrix by the light projection and model view matrix acquired in earlier steps and transpose the result to produce the final texture matrix we'll use to map the texture onto objects in the scene.

Once we have the texture matrix, we're ready to apply the texture. First of all, the texture should be bound as follows:


glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY)
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)


Now we can draw the objects in the scene with the usual dizzying array of OpenGL calls:


if not ambient_shadow_ext:
# do an ambient pass
...

glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light)
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_light)

glPushAttrib(GL_ENABLE_BIT)

glEnable(GL_TEXTURE_2D)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
GL_COMPARE_R_TO_TEXTURE)

glEnable(GL_TEXTURE_GEN_S)
glEnable(GL_TEXTURE_GEN_T)
glEnable(GL_TEXTURE_GEN_R)
glEnable(GL_TEXTURE_GEN_Q)
glTexGenfv(GL_S, GL_EYE_PLANE, tex_matrix[0]))
glTexGenfv(GL_T, GL_EYE_PLANE, tex_matrix[1]))
glTexGenfv(GL_R, GL_EYE_PLANE, tex_matrix[2]))
glTexGenfv(GL_Q, GL_EYE_PLANE, tex_matrix[3]))

... render objects in scene

glPopAttrib()


The most interesting part of the above is the fact we get to use all texture coordinates: S, T, R, and Q. For the uninitiated, only S and T coordinates are explicitly set for more common texture operations which correspond to (row, column) pixel coordinates in the image. The OpenGL programming guide gives a brief overview of the R, Q coordinates and their role in projective textures.

So I only have more hurdles ahead to get this into decent shape for use in most any 3d application. The current form so far is more or less a direct translation of the example from the aforementioned book and inherits its limitations. First of all, by convenience all the objects rendered use color as their sole material for shading. Were we to have textured objects in the scene some additional work would have to be done (namely, better multitexture abstractions in miru or pyglet) and some details regarding this would be incorporated in the resulting API for ShadowMap requiring specification of a texture unit as not to interfere with other multitexture applications beneath the shadow. Next, the current application requires the screen to have dimensions that are powers of 2 - hence, the non-standard resolution of the screenshot above. This is less than ideal of course, but can be remedied by rendering to an FBO - which also calls for some more underlying library work. I've seen some very cool work done with FBOs in the cocos framework and hope to see this sink down into pyglet someday. Otherwise I might try ripping pieces of it out for miru ;)

On a final note, I'm in the midst of some drastic rewriting and reorganization of miru, phasing out the dirty bits--miru.mesh and miru.environment in favor of miru.graphics and miru.context, respectively--and changing, or actually removing, some interfaces to defer directly to pyglet.graphics instead. This means things are proceeding slowly with some subtle breakage here and there. In the long run, I think it will make miru a more coherent and useful library and provide smoother integration for other projects already exploiting pyglet for OpenGL state management.

Friday, August 1, 2008

Iterators are of course not Iterable!

This amuses me to no end:



Iterator iter = properties.getKeys();
for (String key : iter) {
// do something with the key
}

The above java will not compile, and the error is "Can only iterate over an array or an instance of java.lang.Iterable." Apparently an Iterator isn't iterable! Wow! Isn't that so convenient?