setuptools breaks PYTHONPATH

setuptools and egg files are a great way to distribute Python packages and programs. But today I stumbled over a really braindead design decision: setuptools overrides Python’s standard module search path algorithm in a very inconvenient way. Normally, when Python looks for a module or package, it first looks in the current directory, then in any path specified in the PYTHONPATH environment variable, and finally in the default search path. setuptools changes the lookup order to the following:

  1. the current directory
  2. egg files specified in PYTHONPATH
  3. egg files in the default search path
  4. regular modules in PYTHONPATH
  5. regular modules in the default search path

This means that an egg file in the default search path overrides a non-egg module in PYTHONPATH. I wonder what the rationale for that decision is (and it seems to be a deliberate decision). PYTHONPATH is a means to override the default search path and is perfectly suited for testing, developing, and forcing to use a particular (e.g. a bundled) version of a package. Not anymore! Now, the only way to override an egg is to create your own egg. And this is non-trivial and often means an extra step during development.

Here is where it bites me: I have created a library of custom classes, that I use in several projects. I break API frequently, since it is developed in a just-in-time fashion: whenever I need some functionality, I implement it. Therefore I bundle the library (or parts of it) with most projects. This works well for projects that go into a separate directory, like web projects. Nevertheless I use the library for some other projects that are installed system-globally. Otherwise I would need some way to namespace it for the different projects. Not my idea of fun. Normally, this is not a problem, since I can just set PYTHONPATH for all projects that have a local copy, at least when developing. Due to the strange path handling of setuptools, this method breaks and I have to work around it. Great …

gnome-keyring with Python

The documentation on gnome-keyring I discovered helped me to access it successfully with Python. I’ve written a small module that fetches and stores a username and a password for some server.

Some notes:

  • The attributes are freeform, but there a some common attributes for network hosts. These are: “user”, “domain”, “server”, “object”, “protocol”, “authtype”, and “port”. Actually there is a convenience API for network hosts.
  • libgnome-keyring requires that the application is correctly set using g_set_application_name. Unfortunately the Python module gnomekeyring does not do that for us and pygtk does not provide a function to set the application name. Therefore you need to include the module gtk to make it work. (Filed as bug 460515.)Update: g_set_application_name was added to pygtk in the 2.13 development version.
  • Python’s find_network_password_sync function does not allow None to be passed as arguments. The C API allows NULL to be passed here. This means that this function is basically unusable in Python, since you never want to provide all values. (Filed as bug 460518.)

Documentation for libgnome-keyring

I was trying to find documentation for libgnome-keyring for little project I am writing, which accesses a password-protected web service. Unfortunately there is no real documentation for it. No API documentation (well, there are a total of two functions documented), no tutorial. Finally I found this document in GNOME’s Subversion repository. Better then nothing, but why isn’t this part of the API documentation?

Filed as bug 460475.

Update: It seems that libgnome-keyring has gained documentation a few weeks ago, so far only found in Subversion. Kudos to the maintainers!

Selection in GtkTextBuffer

I’ve recently played around with GtkTextBuffer. It’s a rather nice text editing widget (or rather widget part). Unfortunately it misses one functionality, which is also missing from GtkEditable derived widgets: A signal for selection changes. There are two workarounds:

  1. You can setup a notification on the “has-selection” property like this:
    buffer.connect("notify", on_buffer_notify)
    
    def on_buffer_notify(buffer, prop):
        if prop.name == "has-selection":
            if buffer.get_has_selection():
                ...
            else:
                ...
    

    This method only works if you are interested in whether there is a selection or not, not if you are interested in any selection changes. Also, this method works only for GtkTextBuffers, not for GtkEditables.

  2. Tomboy uses a timeout to check the selection status:
    namespace Tomboy
    {
    	public class NoteWindow : ForcedPresentWindow 
    	{
                    ...
    		public NoteWindow (Note note) : 
    			base (note.Title) 
    		{
                            ...
    			// Sensitize the Link toolbar button on text selection
    			mark_set_timeout = new InterruptableTimeout();
    			mark_set_timeout.Timeout  = UpdateLinkButtonSensitivity;
                            ...
                    }
    
                    ...
    
    		void UpdateLinkButtonSensitivity (object sender, EventArgs args)
    		{
    			link_button.Sensitive = (note.Buffer.Selection != null);
    		}
    
                    ...
            }
    }
    

    This method works, but seems a bit complicated, when a simple signal should do the trick.

Filed as bug #448261.

German Card Translations

German card translations are a sad affair. They are riddled with clumsy translations, especially in the card names. It’s not such a great idea to translate english card names 1:1. The rules texts are translated sanely, i.e. using special German templates, although some of the template choices are a bit strange and more verbose than needed. For example, “target” is translates as “deiner Wahl” (“of your choice”), while “of your choice” is translated with “die du bestimmst” (“that you choose”).

In Planar Chaos Ovinize was translated as “Verhammelung”. First of all this sounds strange. The noun “Hammel” was turned in the verb “verhammeln”, which was turned back into the noun “Verhammelung”. But it’s also not good templating. Ovinize is an Instant, and Instants and Sorceries usually get a verb as name. Ovinize is a verb. The German translation “Verhammelung” on the other hand is a noun. This shows a lack of understanding of magic card naming on the part of the translator. “Verhammeln” or “Schafen” had been a much better name.

But what’s worse are wrong translations. For example the German version of Necrotic Sliver has the following ability: “All Slivers have ‘3, Sacrifice this creature: Sacrifice target permanent.'” (You can only sacrifice your own permanents.) Since Wizards decided not to publish Oracle texts until Monday after the prerelease, I could not confirm that the card was actually a mistranslation and had to rule it by its German text. Unfortunately the mistranslations (in the rules text) average around one per expansion.

Finally, cards that have obvious templating problems in the original are not corrected in the German version. For example the flip side of Saviours of Kamigawa’s Erayo, Soratami Ascendant reads “Counter the first spell played by each opponent each turn”, a rather obvious templating error. This was unfortunately not corrected in the German translation. In my opinion a good translator should catch errors like this.

Planar Chaos Prerelease

Today I head-judged the prerelease of Planar Chaos, the latest Magic: The Gathering expansion. The prerelease at the FUNtainment Game Center here in Berlin was attended by 71 players, a fairly disappointing number after the strong Time Spiral prerelease. Some people blame The Burning Crusade, the latest World of Warcraft expansion, out this week. Personally I also think the traditionally weak month of January plays a fairly important role.

Speaking of World of Warcraft: One of the most funny cards of the new expansion is Ovinize, the color-shifted version of Humble. It allows you to “sheep” a creature, essentially remove all its abilities temporarily. One the one hand this resembles the old card Ovinomancer, which created Sheep tokens, on the other hand it plays with a similar concept in MMORPGs like World of Warcraft.

The color-shifted cards are an interesting concept: Reprinting old cards in another color, where they could have been printed if a few decisions had been different.

I had a few interesting calls:

  1. The interaction between Vesuvan Shapeshifter and Shaper Parasite. The Shapeshifter is turned face up and copies the Parasite. Question: Does the “Turned-face-up” ability of the Parasite trigger on the copy. I ruled in analogy to comes-into-play abilities and copy effects: The creature has all the copied characteristics before it is turned face up, so all triggered abilities that trigger on it being turned face up will trigger. Later the Time Spiral FAQ confirmed my ruling.
  2. I botched the interaction of Ovinize and Vanishing. The rules text for Vanishing reads in part (according to the Rules Primer):

    502.60. Vanishing

    502.60a Vanishing is a keyword that represents three abilities. “Vanishing N” means “This permanent comes into play with N time counters on it,” “At the beginning of your upkeep, if this permanent has a time counter on it, remove a time counter from it,” and “When the last time counter is removed from this permanent, sacrifice it.”

    For some reason I assumed that the third ability was included in the second one like this: “At the beginning of your upkeep, if this permanent has a time counter on it, remove a time counter from it. When the last time counter is removed from this permanent, sacrifice it.” Now the question was some like: “If I play Ovinize on a card with Vanishing and one time counter in response to Vanishing’s first triggered ability, what happens?” My ruling was that the last time counter is removed and the card with Vanishing is sacrificed, but any “leaves-play” abilities on the card don’t trigger. The correct ruling is that the last time counter is removed, but the permanent remains in play. At the time the counter is removed, the permanent has no abilities, in particular it doesn’t have Vanishing and so no “Last counter removed, then sac” ability.

  3. Player A had played Hunting Wilds. Some time later his opponent, player B, noticed that A’s graveyard was empty. A looked through his hand, and library and found a copy in the latter. Since he wasn’t sure whether he played one or two copies he wanted to see his decklist. After a short lecture that a player should under no circumstances look through his library without asking a judge first, I fetched the deck list and determined that the player only had one Hunting Wilds in total, so this one had to be the one played earlier. After a brief interview I was convinced that this was an honest mistake and the card had been shuffled into the library when the Forests had been searched for as part of resolving Hunting Wilds. The card was placed in the graveyard, the library was shuffled, and I issued a Warning for Procedural Error – Major.A case could be made for leaving the card in the library (leaving the game state as is is the default remedy if a decision point has been passed). But since the players had placed the card in the graveyard themselves in mutual agreement before a judge was called, I considered this to be the solution both would be more comfortable with, and let the card remain there. I think it was Scott Marshall who proposed to use a remedy both players of a match agree on instead of the normal remedy if this seems suitable. While I was initially opposed to it, this was a good example where this makes sense. I am still undecided on the issue, though.

New Python unittest module?

Collin Winter blogs about an updated unittest module he wrote. His update fixes the internal structure, and therefore the expandability, of the module, but also cleans up the external API. There are still a few minor improvements I would like to see, but nevertheless I hope that his updated version will be included in Python’s standard library eventually.

Java File API

I don’t like Java’s File API. It’s main problem is that is mixes several responsibilities into one class. One responsibility is handling of “abstract” file and path names, i.e. operating system independent file names. The other responsibilities are all file system related: Querying file meta information (access rights, access times, file type), creating files, and listing directory contents. In my opinion these should be clearly separated.

I am also missing a constructor or construction method that creates a File instance from a list of path components like this:

  File f = new File("/", "path", "to", "file");

Maybe I should start a Java warts page, similar to my Python warts page …

Disqualifications & Coverage

I am glad to see that there is an extensive article about the double disqualification of Amiel Tenenbaum and his opponent in Wizards’s coverage of Worlds 2006. I just had the discussion with another judge on #mtgjudge, whether this should be covered and how extensive.

I think covering DQs has several advantages:

  • It generates interest in the game. People like gossip. I know I do. Personally I couldn’t care less about play-by-play coverage, but I’m interested in stuff that happens at these events. I want stories. I want gossip. I want photos.
  • There is a professional interest for me as judge. It shows me how people cheat or try to cheat. It shows things than can happen that lead to disqualifications.
  • It shows that judges catch cheaters. If you read Magic-related message boards (and manage to keep your sanity) you will notice that many players, especially more casual ones, believe that cheating is rampant and cheaters are never caught. Making disqualifications public can help to dispell this myth. It shows that the tournament organizing staff will catch cheaters and that they will be penalized.
  • Word gets around if a player is disqualified. Whether it’s in the official coverage or not. It’s better to have an informed article with interviews with judges involved or the even the players. Otherwise people will speculate and lots of false rumors are started.

On the other hand the coverage still hasn’t got enough photos. And I would still prefer a blog from a coverage reporter to the “Pro Players Blog”, which is just another form of (boring) play-by-play coverage.