Monthly Archives: May 2010

Poland – Part 11: Journey into Auschwitz, and Adventuring Alone in Krakow

Gathering Dust

It’s been about 5 – 6 months since my last Poland entry.  There are a myriad of excuses for this:  tough school year, busy Xmas holiday, relentless work load…

But I have to say I’ve kind of been avoiding writing this one on purpose.  Why?

Well, for starters, I don’t have any photos.  Long story short, before we got off the bus at Auschwitz, we were told there was no photography, so I left my camera on the bus.  Then it turned out that there was no photography in the buildings, so I missed out on getting some snaps outside.

I’ve been able to get my hands on some photos.  A big thanks to Alex Rubin and Anj Mulligan for letting me use theirs.  I’m not entirely sure how using someone else’s photos will affect my narrative, but we’ll see.

The other reason I’ve been avoiding this one is because I wrote so damn much about it.  39 pages from my journal were devoted to this day.

Why so much?  Well, to be honest, it was a pretty emotionally charged day.  A lot of people were crying during the tour.  My reaction was just to write down everything I could see and hear, as fast as I could.  I hope I got everything right.  Please correct me if I’ve gotten something wrong.

Anyhow, enough stalling.  Here we go.

June 24, 7:45AM

It was an early morning.  I showered, shaved, sent some email, and then hung out in the kitchen/common area with Yev, eating some cocoa-puffs while she boiled water for tea.

The breakfast lady was in a foul mood that morning.  She stormed in to the kitchen and started rearranging things with a violent efficiency, clicking her heels.  Yev and I were silent.  Finally, I said “Dzien dobry” (good morning) to break the tension.

Wow.  That was the last straw, I guess.  The breakfast lady flew into a huge Polish rant as she stormed around us.  We couldn’t understand a word, but she was clearly upset.

Yev said she reminded her of one of her Soviet schoolmasters.

I didn’t wait to see how the fury played out.  I got out of there.  Yev stayed behind.

Yev later told me that, after making a sandwich (which the breakfast lady saw her do), she made a super-quick pit-stop at the washroom, only to come back and find that her sandwich had been thrown in the garbage.  Presumably by the breakfast lady.

8:25AM

We boarded the bus and were en route.

It was a tense morning.  Tamara told us that the Auschwitz trip was optional, and so a few of us had stayed back.  The bus ride was unusually quiet.

I think everybody was preparing themselves.

9:45AM

I wasn’t allowed to bring my camera (or so I thought), so I left it on the bus.

After getting off the bus, we read a multi-lingual sign that set the behavioural tone for the rest of the tour:

Througout the world, Auschwitz has become a symbol of terror, genocide, and the Holocaust. The German forces occupying Poland during the Second World War established a concentration camp, on the outskirts of the town of Oswiecim. In 1940, the Germans called the town Auschwitz and that is the name by which the camp was known. Over the next years it was expanded into three main camps: Auschwitz I, Auschwitz II-Birkenau, and Auschwitz III-Monowitz and more than forty subcamps.

The first people to be brought to Auschwitz as prisoners and murdered here were Poles. They were followed by Soviet prisoners of war, Gypsies and deporters of many other nationalities. Beginning in 1942, however, Auschwitz became the settling for the most massive murder campaign in history, when the Nazis put into operation their plan to destroy the entire Jewish population of Europe. The great majority of Jews who were deported to Auschwitz – men, women, and children – were sent immediately upon arrival to death in the gas chambers of Birkenau.

When the SS realised that the end of war was near, they attempted to remove the evidence of the atrocities committed here. They dismantled the gas chambers, crematoria, and other buildings, burned documents, and evacuated all those prisoners who could walk to the interior of Germany. Those who were not evacuated were liberated by the Red Army on January 27, 1945.

On July 2, 1947, the Polish Parliament established the State Museum of Oswiecim – Brzezinka on the sites of the former camps at Auschwitz I and Auschwitz II-Birkenau. In 1979, these camps were formally recognized by UNESCO by their inclusion on its World Heritage List.

PLEASE BEHAVE APPROPRIATELY RESPECTING THE MEMORY OF THOSE WHO SUFFERED AND DIED HERE.

Next to this was a map of the compound.  Again, no photos, so something like this will have to do.

Looking at the map, my eyes were drawn to the familiar word “Canada”.  It turns out that, when new arrivals came to the camps, their belongings were stripped from them and sent to a special area of the camp called Canada for sorting and searching.  It was called Canada, because at the time, Canada was considered the land of plenty.  Here’s Wikipedia’s take on it.

Near the signs were, of all things, gift and souvenir shops, called the “informatory”.  Postcards, books, videos, photos…  seemed a bit in bad taste.  After seeing the gift shops, I noticed all of the smiling tourists around me, and I found that quite macabre.

It was particularly disturbing because of how quiet it was.  There were also “keep silence” signs all over the place.  So yeah, it was quiet.  Really quiet.

As we approached the entrance, we heard birds chirping.  It was overcast – the grass was still wet from the morning dew.

10:00AM

As we were reading the signs, Tamara had gone off to get the tour guide.  On her way back, her face was covered in tears.  She’d visited Auschwitz for a tour several times before, and firmly stated to us that she couldn’t bring herself to do it again.  So she went off to go wait in the bus.  It was an ominous moment.

All of the tour guides were dressed in black.  Ours was no exception.  After a brief, quiet hello, she gave us each a set of earphones and receiver.  This is how she would communicate with us during the tour.  This way, she wouldn’t have to yell for us all to hear her.  Instead, the tour became very personal, and she was able to speak softly to each of us individually.  I wrote in my journal that her voice was incredibly soft, caring, and soothing – and that she reminded me more of a nurse than a tour guide.  I really think part of her job was to soothe, as well as to educate.

We headed towards Auschwitz I.

[simage=679,288]

An Extremely Brief History of Auschwitz

Auschwitz I was the original concentration camp, and eventually became the central administrative hub of the complex.

[simage=680,288]

The buildings of Auschwitz I were military barracks, originally constructed by and for the Polish army.  In the late 1930’s, Poland had been invaded, split up, and annexed to the Nazis and the Soviets.  So technically, Poland ceased to exist.  The Nazis saw the barracks in their new territory as “very convenient” for housing the growing number of Polish prisoners, especially considering the railroad junctions that led to it.  The Nazis set up shop, and the land and buildings became Auschwitz I.

[simage=681,288]

Auschwitz I was originally established strictly for Polish prisoners, but eventually Gypsy’s and Soviet POW’s were held there as well.

Eventually, Auschwitz I got so packed with prisoners, that two more camps were built in close proximity.  Those camps were named Auschwitz II and III.

The Main Gate

This is the main gate to the camp:

[simage=715,288]

The gate reads:

Arbeit Marcht Frei

Which translates to “Work Makes You Free”, or “Work Gives You Freedom”.

Surrounding the entire camp was a double electric fence:

[simage=717,288]

Entering the Camp

[simage=714,288]

The camp orchestra, composed entirely of prisoners, would play lively German marches as the prisoners were led into the camp.  It was humiliating and dehumanizing.  This also made it easier for the guards to count and keep the prisoners in step.

The men and women were then separated, and sent to different barracks.  There would be 800-1000 prisoners assigned per barrack, which only had 2 stories.  The prisoners in Auschwitz I were cramped to the extreme.

[simage=719,288]

The roads we walked down were all empty and quiet, but it wasn’t hard to imagine them filled with the noise of thousands upon thousands of prisoners, being crammed into the buildings.

It was pretty disturbing.  In this shot, you can see me scrambling to scribble all of this information down in the background.

[simage=686,288]

We then entered one of the barracks, which had been converted into a museum.

[simage=688,288]

A sign loomed overhead reading:

The one who does not remember history is bound to live through it again.

The Barracks

Auschwitz was almost in the center of occupied Europe.  With the already-established railroad system, the Nazis were able to send over 1,000,000 prisoners to Auschwitz.  The majority of those prisoners were Jewish.

It didn’t start out that way, but at some point during 1942-1943, Auschwitz became an extermination camp.

A sign on the wall broke down the prisoners as follows:

1,300,000 sent to Auschwitz
1,100,000 Jews
140,000 – 150,000 Poles
23,000 – Roma / Gipsy’s
15,000 – Soviet Prisoners
25,000 – Other

90% Jews

A large, glass, transparent urn in the barracks held human ashes in rememberence.

During the original invasion of Poland, the Nazis focused on capturing/executing as many Polish monks, priests, lawyers, leaders, and educated people as possible.  This was their method of “destroying” Poland’s culture and identity.  After an uprising in Warsaw, 13,000 Poles were sent to Auschwitz I as punishment.

Many photos were taken at Auschwitz by the SS for their own use.  Those black and white photos lined the walls of the museum.  We weren’t allowed to take photographs, so I can’t show them to you, but I can describe some of them.  Imagine black and white, blurry photos of extremely thin, extremely gaunt, bald people, wearing prisoner garb.  Imagine seeing photos of them digging graves for themselves, or jumping to a particular height for a guard’s amusement, or running at top speed in a big circle “just because”, so the guards could watch.

SS “doctors” were always present at prisoner arrival to “conduct selections” on who could work and who could be executed immediately.  There were photos on the wall of women, children, and old people, being sent to their death.  They look calm, because they didn’t know.

The Jews who weren’t executed immediately were put to work.  Some were sent to Auschwitz III, which was a work and manufacturing camp.  Prisoners were forced to make things there for the Nazis.

Other prisoners became Sonderkommandos, which means they assisted in the execution of other prisoners.  Sonderkommandos would work in the crematoriums and gas chambers, and were forced to witness and commit various horrible atrocities against other prisoners.

Gassing of prisoners took place underground.  A single gas chamber would have 2000 prisoners crammed inside of it at one time.  Prisoners who entered the gas chambers were told that they were taking showers.  Fake faucets in the ceilings and walls helped sell the illusion.

After the doors were shut, crystals of Cyclone B were dropped in through openings in the ceiling.  After 20 minutes, all were dead.  Sonderkommandos would then go in and carry the bodies to the crematorium.

Before the bodies were cremated, Sonderkommandos had to cut off the hair from the women.  The hair was packed into bags, and sent elsewhere to be turned into hair-cloth and other textiles.  The ashes of the prisoners were used as fertilizer.  Everything was reused.

At one point, we entered a room in the museum, where behind a large pane of glass, we saw mounds of human hair that had been found at the camp.  Massive quantities of dead prisoners hair.

This was the point in the tour that most people started to lose it.  Lots of tears.  Lots of crying.  I kept scribbling.

Any belongings or valuables brought by the prisoners into the camp were sent to the camps called Canada I and Canada II for processing.  The plunder ended up being part of the evidence that was used to prove the atrocities that had happened at the camp.  Like the piles of hair, we saw piles of glasses, piles of shoes, piles of Jewish prayer shawls, combs, brushes, suitcases, clothing, prosthetics, crutches, pottery, bowls, cutlery… everything was sorted.  The quantity was simply horrifying.

In my journal, I noted that the lighting in the barracks was quite muted, but that the exhibits (the hair, combs, etc) were under bright flourescents.  It was really macabre – like seeing a body at a morgue.

The next part of the exhibit was even more horrifying.  It turns out that 20% of the victims of the camp had been children (90% Jewish).  There was a room, absolutely packed to the brim, with children’s shoes.  So many shoes.

And that’s the thing – I noted this in my journal:  it’s not just the atrocity itself, but the sheer size of the atrocity that is so horrifying.  The piles of shoes and the hair really gave us a sense of that size.

Prisoners

Of the prisoners that weren’t immediately executed, 50% were Jewish.  Many were Polish.  All were treated like property.

There were some prisoners who were given some of the responsibilities of the guards – for example, being in charge of work units.  These prisoners were always German criminals.

The prisoners were deprived of all of their human characteristics.  No names.  Just numbers.  Photos were originally used for identification, but this was eventually changed to tattoos because a prisoner’s appearence would change too much.

The Nazis were meticulous record-keepers.  Prisoner IDs were linked to prisoner files that held details such as education, age, and history.

Hunger was rampant among the prisoners.  There wasn’t nearly enough food for all of them.

One sign we saw gave us a breakdown of the daily life of a prisoner.  I couldn’t get it all down, but the pattern was obvious:  prisoners were slowly killed with work.  They were punished and beaten.  Most lasted less than a year.

All non-Jewish children became prisoners.  These children were also often subject to horrific “scientific” experiments by Dr. Josef Mengele.

Among other things, Mengele apparently wanted to find ways of creating twins and triplets, so that German “Aryans” could reproduce quickly.

Other atrocities were performed by Dr. Carl Clauberg who tortured Jewish women, in an attempt at finding ways of sterilizing them.

It was a lot to take in.  We went back outside.

Back Outside

We were at the execution wall.

[simage=718,288]
[simage=689,288]

Prisoners, often naked, were shot in the back of their heads.  It is estimated that 10,000 prisoners were shot at this wall.  There were also posts were prisoners could have their arms strung up behind them for hours, as torture, and as punishment.

There were also starvation cells.  In one of those cells, Saint Maximilian Kolbe was starved to death with 9 other men.

Eventually, we entered a building where the first experimental mass killings took place.  There were suffocation cells.  There were cells where prisoners were forced to stand all night.  Pretty horrific.

The “camp hospital” existed for propaganda, to keep the purpose of the extermination camp a secret.  The hospital was really the “crematorium waiting room”, since selections would often happen there.

Roll call was also used as prisoner punishment.  If a prisoner escaped, or it was suspected that a prisoner had escaped, the remaining prisoners would be punished.  They’d be lined up and counted outside of their barracks, again and again.  Sometimes they’d be out there for 20 hours straight.

Only 144 prisoners successfully escaped Auschwitz.  Captured escapees were tortured for information on their escape, and then executed.

Crematorium I was originally an ammo bunker.  The crematorium was dark…stone…dusty…gritty.  It was all so much monstrous efficiency.

Break

The first part of the tour was over.  We handed back our headsets and took a 10 minute break.

I wrote that the sun was warm, and that some of us were hungry.

12:05PM

We just got a small snack.  We’re all sitting outside.  Everybody is quiet.  Some of us are eating.  Some of us are drinking coffee.  Some of us are smoking.  Some of us are crying.  It’s pretty rough.  It’s hard to be an optimist here – hard to feel good, anyhow.  Just…devestated.

12:20PM

We’re late.  Our 10 minute break went on too long, and we’re late getting back on the bus.  We’re heading to the next camp.

The bus really has never been so quiet.  But what do we say to one another?  This is no place for joking around…no place for making quips.  What’s the first thing you say?

There are storm clouds in the distance.

It’s a 3km drive to the next camp.  Tamara says that there are no exhibits…just the barracks and other buildings, the railroad tracks, and the gas chambers.

Auschwitz II

We’re here.  I recognize where I am – I think I had seen it in Schindler’s List.

[simage=721,288]
[simage=722,288]
[simage=701,288]
[simage=705,288]

It’s brick and fields, barbed wire, and wooden barracks.  It’s starting to rain gently.  Those of us with umbrellas put them up.

The fields here used to be Polish homes and farmland before the residents were evicted by the invaders.  The barracks were constructed from materials from destroyed buildings.

Not all of the barracks are still standing.  Some have been dismantled.  Others have crumbled with age.

The gas chambers have been destroyed, but the ruins are still there.

There’s grass and flowers now, but during the war, everything here was muddy and swampy.

We closed our umbrellas and went into the barracks.

[simage=725,288]

The Barracks

[simage=704,288]

The barracks reminded me of stables for horses.  Wooden bunks, and a single stone oven for heating.  At least 400 people per barrack.  No toilets inside.  No washrooms.  Just buckets and ditches in the ground, and barrels of water outside.

There were “toilets” outside, which were really just holes in the ground with wood frames built over them.

Members of the prison resistance would meet by the ditches/toilets, since the guards would never go near them (due to the smell, and disease).

Like Auschwitz I, there’s barbed wire everywhere.

[simage=724,288]

The Gas Chambers, and Liberation

75% of Jews were gassed on arrival to the camp.

In November, 1944, Heinrich Himmler ordered the crematoria destroyed before the Red Army could reach the camp.  Nazi soldiers began destroying the evidence of what had happened at the camp, starting with the gas chambers.

[simage=726,288]
[simage=710,288]
[simage=711,288]

In January, 1945, with the Red Army getting closer, SS command ordered that all prisoners at Auschwitz be executed.  This order was never carried out.  Instead, the camp was evacuated, and the prisoners were sent on death marches to another camp in Wodzisław Śląski.  Prisoners who were too sick or weak to march were left behind.  Those 7,500 prisoners were still there when the Red Army came to liberate them.

According to Wikipedia:

Approximately 20,000 Auschwitz prisoners made it to Bergen-Belsen concentration camp in Germany, where they were liberated by the British in April 1945.

Final Words

Some of the buildings and ruins in Auschwitz II are sinking, and the museum is working hard to restore them.

Just past the last gas chamber is a large stone monument.  Large, Easter Island-like heads and monoliths.

At the base of the monument are numerous plaques, all in different languages.  Here’s the English one:

[simage=727,288]

The monument is surrounded by flowers and wreaths.

The tour is now over.  We thank the tour-guide, and, in the rain, follow the train tracks back out the main entrance.  Birds chirp.  Grass grows.  Puddles. Life continues.

It’s no surprise to me that existentialism and the Theatre of the Absurd came about in reaction to these atrocities.

And that’s it for my Auschwitz notes.

3:30PM

Just got off the bus.  Had a nice long nap – I think most of us did.  We’re back in Krakow.  It’s sunny and warm.  Tamara has given us free time now.

5:30PM

Had a nice big late lunch (or early dinner) with  Alex, Linn, Una, and Jiv.

[simage=492,288]

We discussed Auschwitz – it seemed OK to do now.  We all agreed that it was a devastating experience, but we were glad we did it.

Our moods were starting to lighten.

We also saw a guy in a beer suit walking around:

[simage=486,288]
[simage=487,288]
[simage=488,288]

And we also saw some breakdancers doing some moves in the market square.

[simage=489,288]
[simage=490,288]
[simage=491,288]

I caught some of it on video:

After eating, I went to the phone to call Em.

6:05PM

Just got off the phone with Em.  Told her all about Auschwitz.  Missing her a lot. I decided to try to make myself feel better by getting some lemon sorbet.  It’s a pretty good deal at 4z.  I found Alexi and Yev drinking coffee in the square, and joined them while I finished my cone.

I think Yev borrowed my camera and took these photos:

[simage=493,288]
[simage=494,288]

7:05PM

I’m on my own now.  There’s some big crowd in the square, and I hear clapping.  Buskers?

Ah, looks like another breakdance group.

[simage=496,288]

And there are accordion players here!  They’re playing some classical music.  Nice.

[simage=497,288]

And a mime painted all in gold (though in this shot, he looks like he’s on break):

[simage=498,288]

There’s also a group of people giving out free hugs.

All of this activity is occurring around another statue of Adam Mickiewicz:

[simage=495,288]

7:15PM

I listen to the accordion players for a while.  They’re playing the William Tell Overture.

Eventually, I exit the square into a side street.  I hear violins…eventually, I see the players:

[simage=501,288]

And I think I hear a dulcimer being played somewhere.

7:25PM

The street I strolled down is called Florianska.  At some earlier point, I had gotten the urge to check out some of the local music scene, and the girl at the hostel told me to walk down this street.  She said there was an indie rock bar around here called The Lizard, but I haven’t found it yet.  And I’m slowly approaching the end of the street.

[simage=502,288]

So I haven’t found The Lizard.  My quest to find some indie music is a failure.  I did peer through the window of a closed music store, though.

Heading back, the sun is starting to set.

[simage=506,288]

I’ve suddenly realized that there’s less than a week left in my trip.

7:30PM

I’m sitting in some park, listening to the birds.  While I recognize some of the calls, most of the birds sound really different than what I’m normally used to.

Thunder rumbles in the distance.  I think there will be another storm tonight.

7:50PM

I’m back in the square.  I hear bagpipes somewhere – the notes from the pipes are reverberating off of the walls.

Eventually, I see the piper.  He’s really far away, and I have to zoom in with my camera:

[simage=509,288]

The square seems pretty busy for a Wednesday night.  I imagine the place gets absolutely packed on weekends.

It’s a whole spectrum of age groups out this evening.  I’m also hearing a variety of languages.  Polish, English, and German for starters.  Italian too.  Mostly white people.  One or two exceptions.  Some rollerbladers.

The Trumpet

An ambulance raced by, driving through the crowded square. As it passed, I heard a trumpet playing a tune from the top of the cathedral, and then abruptly stop.

I heard the same tune over the bus radio when we first landed in Poland, but I think I forgot to write about it.  It has something to do with a trumpet player trying to warn the city of invaders, and then being shot in the throat whiel playing – hence, the sudden stop.

A crowd of people has formed in front of one of the cathedrals.  Lots of talk, buzzing, but no English.  Not sure what’s going on.  Is it a tour about to start?  Church service?  Mass?  At 8:07PM?

The Birds

I’ve noticed some noisy, high-pitched, tiny birds flying from building to building.  They’re abundant.  Maybe bats?  I feel like an idiot trying to take a photo of them, but I do it anyways:

[simage=510,288]
[simage=511,288]
[simage=512,288]
[simage=513,288]

The three men playing on the accordions are still there, and now that crowd is starting to move.  I guess the cathedral was acting like some big meeting point for a tour group.  The accordion players are doing the William Tell Overture again – they seem to have a repetoire of about 5 songs.

My notes for the day end there, but I imagine I eventually headed back to the hostel and went to sleep.

Click here to go to Part 11.5: Back to the Hostel

Click here to go back to Part 10: Journey to Krakow, Wawel Hill, and The Dragon.

Python Eggs: Sunny Side Up, and Other Goodies (or How I Learned to Stop Worrying and Start Coding)

Cooking with Eggs

Every now and then, the computer gods smile and give me a freebie.

I’ve been worrying my mind out over a few problems / obstacles for my Review Board extensions GSoC project.  In particular, I’ve been worrying about dealing with extension dependencies, conflicts, and installation.

I racked my brain.  I came up with scenarios.  I drew lots of big scary diagrams on a wipe board.

And then light dawned.

Batteries Come Included

Enter Setuptools and Python Eggs.

All of those things I was worried about having to build and account for?  When using Python Eggs, It’s all built in. Dependencies?  Taken care of. Conflicts?  Don’t worry about it.  Installation?  That’s what Setuptools and Python Eggs were built for!

In fact, it even looks like Setuptools was designed with extensible applications in mind.

Wait, really?  How?

Here’s the setup.py file for the rb-reports extension in the rb-extensions-pack on Github:

from setuptools import setup, find_packages

PACKAGE="RB-Reports"
VERSION="0.1"

setup(
    name=PACKAGE,
    version=VERSION,
    description="""Reports extension for Review Board""",
    author="Christian Hammond",
    packages=["rbreports"],
    entry_points={
        'reviewboard.extensions':
        '%s = rbreports.extension:ReportsExtension' % PACKAGE,
    },
    package_data={
        'rbreports': [
            'htdocs/css/*.css',
            'htdocs/js/*.js',
            'templates/rbreports/*.html',
            'templates/rbreports/*.txt',
        ],
    }
)

Pay particular attention to the “entry_points” parameter.  What this is doing, is registering rbreports.extension:ReportsExtension to the entry point “reviewboard.extensions”.

“Hold up!”, I hear you asking. “What’s an entry point?”

Entry Points

An entry point is a unique identifier associated with an application that can accept extensions.

The unique identifier for Review Board extensions is “reviewboard.extensions”.

This is the first handshake, more or less, between Review Board and any extensions:  in order for Review Board to “see” the extension, the extension must register an entry point at “reviewboard.extensions”.

This blog post shows how extensions can be found and loaded up.

Other Goodies

INSTALLED_APPS and Django

I remember also being worried about how to create tables in Django for extension models.  I thought “holy smokes, I’m going to have to either shoehorn some raw SQL into the extension manager, or maybe even trust the extension developers to write the CREATE TABLE queries themselves!”.

Luckily, there’s a better alternative.

Django knows about its applications through a dictionary called INSTALLED_APPS. When you add a new model to a Django project, you simply add the model app to the INSTALLED_APPS dictionary, and run “manage.py syncdb”.  Django does the magic, bingo-bango, and boom – tables created.

So if a new extension has some tables it needs created, I simply insert the app name of the extension into INSTALLED_APPS when the extension is installed, and call syncdb programmatically.  Tables created:  no sweat.

django-evolution

Creating tables is easy.  But what if an extension gets updated, and the table needs to be modified?  Sounds like we’ve got a mess on our hands.

And don’t expect Django to save you.  When you modify a model in Django, they expect you to into that DB and alter that table by hand:

[syncdb] creates the tables if they don’t yet exist. Note that syncdb does not sync changes in models or deletions of models; if you make a change to a model or delete a model, and you want to update the database, syncdb will not handle that.
From The Django BookChapter 5: Models

Thankfully, there’s a mechanism that’s already built into Review Board that makes this trouble go away:  django-evolution.  Django-evolution, when used properly, will automatically detect changes in application models, and alter the database tables accordingly.  This is how Review Board does upgrades.

And to top that off, RB co-founder Christian Hammond just became the django-evolution maintainer.

Wow.  Everything is falling neatly into place.

Python Metaclasses in Review Board

So, after diving into the Review Board extension code, I hit a little snag.

It turns out I never learned about Python metaclasses.  And the extension code in Djblets / Review Board uses them.  In the map I made of the Review Board extension code, I represented my confusion with an image of some kind of quantum-divide-by-zero implosion.

I’ll get to the why for metaclasses in a second.  First, I’ll demonstrate how it’s currently being used in the code.

The Way Things Are

This snippit is from the Djblets library, in the extensions folder, in base.py:

...

class ExtensionHook(object):
    def __init__(self, extension):
        self.extension = extension
        self.extension.hooks.add(self)
        self.__class__.add_hook(self)

    def shutdown(self):
        self.__class__.remove_hook(self)

class ExtensionHookPoint(type):
    def __init__(cls, name, bases, attrs):
        super(ExtensionHookPoint, cls).__init__(name, bases, attrs)

        if not hasattr(cls, "hooks"):
            cls.hooks = []

    def add_hook(cls, hook):
        cls.hooks.append(hook)

    def remove_hook(cls, hook):
        cls.hooks.remove(hook)

...

And here are those classes being used in the Review Board extension directory, where it defines its hooks:

...

class DashboardHook(ExtensionHook):
    __metaclass__ = ExtensionHookPoint

    def get_entries(self):
        raise NotImplemented

class NavigationBarHook(ExtensionHook):
    """
    A hook for adding entries to the main navigation bar.
    """
    __metaclass__ = ExtensionHookPoint

    def get_entry(self):
        """
        Returns the entry to add to the navigation bar.

        This should be a dict with the following keys:

            * `label`: The label to display
            * `url`:   The URL to point to.
        """
        raise NotImplemented
...

The idea here is that, if someone wanted to write an extension, they could add a hook to the navigation bar by subclassing the hooks, like so:

(from the rbreports prototype extension)

...

class ReportsDashboardHook(DashboardHook):
    def get_entries(self):
        return [{
            'label': 'Reports',
            'url': settings.SITE_ROOT + 'reports/',
        }]

class ReportsExtension(Extension):
    is_configurable = True

    def __init__(self):
        Extension.__init__(self)

        self.url_hook = URLHook(self, patterns('',
            (r'^reports/', include('rbreports.urls'))))

        self.dashboard_hook = ReportsDashboardHook(self)
...

So you can see the __metaclass__ thing up there in the DashboardHookDashboardHook subclasses ExtensionHook, and has ExtensionHookPoint as a metaclass.

Why? And what is a metaclass anyways?  Why is this useful at all?

Well, luckily, I think I’ve figured it out.

Behold – Metaclasses

Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
— Python Guru Tim Peters

There are plenty of resources available regarding Python’s metaclasses.  And I ended up reading a ton of them trying to figure out just what the hell metaclasses actually do.

As it turns out, the best explanation I got was from Wikipedia:

In object-oriented programming, a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain classes and their instances.

In particular, check out their example on Cars.  That, for me,  more or less set the record straight on how metaclasses are used.

But why does Review Board use them when defining those hooks, like DashboardHook?

Why?

Forget the metaclasses for just a second.

Something is missing from that code that I posted up.

I’ll pose it as a question:  How exactly is Review Board supposed to know about ReportsDashboardHook?  Like…if we’re rendering the Dashboard, and want to fire off calls to all of our DashboardHooks…how do we do it?  How can we find all of the active extension subclasses for DashboardHook?

One way you could do it, is to dive into the extensions directories, and use regular expressions to find defined classes.  That sounds like a lot of work, brittle, and prone to error.  What else?

Next, you could go back to those extension files, “eval” them (shudder), and extract the globals(), looking for ExtensionHook subclasses.  That also sounds like lots of work, brittle, and prone to error.

You could go for class.__subclasses__…but this gives you every subclass, and we just want the hooks that are for active extensions.  We could filter out all of the ones that aren’t activated, but we’d have to do that for every hook, and that blows.

Next, we could have developers add their hooks to a registry after defining them.  So, we could have:

class ReportsDashboardHook(ExtensionHook):
  # ...
  # code goes here
  # ...

dashboard_hook_list.append(ReportsDashboardHook)

And then when an extension is shut down, we just remove it from the global hook list.  That doesn’t look so bad, does it?  The only problem, is now we have to trust that extension developers will know to add those hooks to the global_hook_list?  It’s another step, another thing to forget, and another point of failure.

And it seems silly – I mean, why go through all this effort?  Review Board has already seen these hooks…why should it be so hard to just access the list activated hooks easily?

And there it is. That’s why I think we’re using metaclasses.

If you go back to ExtensionHook and ExtensionHookPoint, you’ll notice that ExtensionHookPoint has a list as an instance variable called hooks.  That’s the global hook list for that class of hook (DashboardHook).  And then ExtensionHook, in its constructor, automatically adds itself to the Extension’s internal list of hooks, as well as the global hook list, with the add_hook method.  Shutting down the hook removes that hook from the global hook list.

So now, all we need to do to add our implemented hook to the list of hooks, is to simply subclass DashboardHook, or NavigationBarHook, or any of those Review Board hooks.  No need to add the hook to the global list manually – Djblets / Review Board will take care of it.  So now, we can get all of the DashboardHook subclasses for activated hooks, easily and consistently, with no extra work for the extension developer.  Same with the NavigationBarHook subclasses.

Smooth as glass.  Nice.

Anyhow, that’s my understanding of it.  If I’m totally off, I’ll let you know when I find out.

The Review Board Extension Life-Cycle

According to the timeline, I’m still in the community-bonding period for GSoC.  Coding for my project is supposed to start sometime towards the end of May.

So I’m using the time to do the following:

  • Close as many small, easy tickets as I can for the  upcoming Review Board release.  I’ve already posted some patches for review.  More forthcoming.
  • Get to know the tools I’ll be using.  Review Board is hosted on Github, and I’m relatively new to the whole DVCS thing.  I’ve been figuring out how to use Git, how to post patches, merging, branching, etc.
  • Get to know the area I’ll be working in.  I’ve been figuring out how Django apps organize themselves.  I’ve also drawn up a map of the current state of the extension framework to help me visualize it.
  • Get to know the other developers working on Review Board.  I’ve been hanging out in the #reviewboard-soc FreeNode IRC channel.  Very nice, and helpful people to work with.
  • Develop a plan of attack for my project

And this last point is the one I want to talk about.

The Review Board Extension Life-Cycle

An extension isn’t just some isolated piece of code that gets crammed into an application.  When you’ve got multiple extensions already installed and running, installing and activating a new extension is like introducing a new animal into an ecosystem.  You have to make sure that your new animal plays nice with the others, and that, in the morning, there won’t be a pile of rotting corpses where your application used to be.

I may have gotten carried away with my metaphor.

So let’s look at what I’m envisioning as the life-cycle for a Review Board extension.  I’ll start right from the top.

Getting the Extension

Ideally, this will work as nicely as WordPress’s implementation:  a Review Board administrator is given a catalog of extensions to choose from within the Administrator interface, and one click later, the desired extensions are downloaded and ready to be installed.

For all you system administrators out there, that last idea might make your toes curl.  A Review Board administrator is not necessarily a system administrator, and the system administrator knows what he/she likes on their machine.  An application that can go and download other applications can be dangerous.  We have to ensure that the application that we’re downloading is the one we’re trying to download.  The last thing we need is some man-in-the-middle to do something cute and bork the code review machine.  And we want to ensure that the extension functions as advertised.  No hidden features.  No self-destruct mechanisms.  No back doors.

Do I have a plan for this part?  Well….no, not really.  I don’t imagine I’ll get that far – I consider it a little out of my scope.  So, for my project, I think it’ll satisfy if the system admin (or Review Board admin) can manually download the extension, decompress it, and place it where Review Board can work with it.  That other stuff can come later (and I’ll try to design so that it can come later easily).

Installing the Extension

Ok, so at this point, we’ve got our extension downloaded in a place where Review Board can see it.

So now what?

Now we need to install the extension.  To me, that means letting the extension put its roots into the RB install by creating database tables, preparing initial data, and generally doing everything to make conditions suitable for the extension to function.

When an extension install begins, I imagine it will consider the following questions (in no particular order):

  1. Do I (the extension) depend on other extensions to function?  If so, are those extensions present?  If not, let the user know so that they can go get them.
  2. Are there some extensions already installed that will conflict with me, or make me behave badly?  If so, let the user know so that they can either remove that conflict, or find an alternative extension.
  3. Is the user entirely aware of what I can do?  Make sure that my capabilities, limitations, and behavioural quirks are known to the user.

Once those 3 questions are answered, and everything is looking good for the install, the extension will create the database tables it needs (if any) in order to function.  If anything goes wrong during this process, the database changes will be rolled back, and the user will get a full read out about what went wrong.

If nothing goes wrong, the extension will be installed.  The user might then be asked to set some initial operating parameters for the extension.

Ok great – the extension is installed.  Now what?

Activating the Extension

For something that sounds so dramatic, the explanation about what happens is pretty short:  Review Board simply becomes aware that the extension is activated, and passes data through the necessary hooks in order for the extension to function properly.  Upon activation, the extension should do a quick double-check to ensure that all prerequisites for the extension have been met.  This is because we can’t trust users to activate an extension immediately after downloading them.

The Extension Runs

While it’s activated, the extension will probably react to various events that happen on Review Board.  Tables will be updated.  View methods will be run.  Templates will be rendered.

If anything, ever, goes horribly wrong with an extension, the following will happen:

  1. A log entry will be written, dumping the error, and information about what the user was trying to do
  2. An error message will be displayed to the user, trying to tell them what exactly happened
  3. The extension (and its dependents) will be deactivated.  They will no longer react to events on Review Board.  Their tables will still be there, the settings will still, but the extension will be, in essence, asleep.  Dormant.  Non-reactive.

The administrator could try to reactivate the extension at this point.  They might try to contact the extension developer for support.

The Extension is Un-installed

If the user wants to rid themselves of an extension, they must first deactivate it.  This will put it (and its dependents) in the dormant state.  It just switches them off, nothing else.

Deactivated extensions can then be un-installed.  If the user chooses to un-install the extension, the extension database tables and settings will be wiped out.

The extension itself won’t be deleted though – at least, not within the scope of my project.  The extension files will need to be removed from Review Board manually.

At this point, any dependents that this uninstalled extension had will no longer be able to be activated.

Anyhow, that’s how I envision the life-cycle.  It’s my first go at it, so I’d love to hear some feedback if you have any.