getting close …

We’re getting very close to releasing zmugfs 0.1, the read-only release. As you may recall on Monday I got zmugfs to show imgdata in nautilus.

img data

But I could only get it to work if I used the thumbnail sized imgdata. In the read() method I was logging into smugmug, reading the thumbnail imgdata, logging out of smugmug, getting the imgdata via http GET, and returning. Well, this is crazy because read gets called multiple times with different offsets. So I ended up implementing the open() and release() methods. In the open, I get the imgdata (original size) via the smugmug api and store the imgdata in an image cache (an in memory dictionary). In the read method, it now respects the offset and size attributes by reading from the image cache. Then in the release method, I delete the entry from the image cache. I know not much of a cache. 🙂 But it solved my problem. I’m putting the cache work off a bit, not sure how to approach that yet.

Next thing I wanted to try all of the command line utils (at least the ones I’ve used before) to see how they react to zmugfs. I was pleasantly surprised with the result.

command result comment
basename SUCCESS  
cat SUCCESS  
chgrp FAILURE chgrp method not implemented
chmod FAILURE chmod method not implemented
chown FAILURE chown method not implemented
cp from SUCCESS able to copy image from smugmug using zmugfs
cp to FAILURE mknod not implemented
file SUCCESS got appropriate file type
ln -s SUCCESS softlink created
ls SUCCESS listing appears correctly
mkdir FAILURE mkdir method not implemented
mv FAILURE copy part worked; unlink not implemented
rm FAILURE unlink not implemented
rmdir FAILURE rmdir not implemented
touch FAILURE setattr not implemented

From a readonly perspective, it is looking great. What’s left before a release you ask? Just a few things.

  • packaging
    • write spec file
    • figure out setup.py
  • design a logo
  • move code to zmugtools for branding purposes
  • work out image cache

That’s it, once I finish those things zmugfs 0.1 will be released out into the wild! I’m really excited as this is my first ever open source project. I’ve submitted patches to a few things, but never anything this extensive.

permissions and time

Tonights zmugfs update is permissions and time are now shown correctly. I defaulted the user and group to that of the user running the program (in this case me) 🙂 And I now show the date and time correctly from the api.

Update: by “in this case me” it doesn’t mean I hardcoded my uid and gid. I used os.getuid() and os.getgid()

By the way, disregard the drew and foo entries. Those are debug strings which will be removed.

perms and time

zmugfs shows image data

Success. I got zmugfs to show image data in nautilus. It is a horribly inefficient algorithm but it works. I do the following:

  • login to smugmug
  • getUrls for imageid
  • httpget image from above url
  • return imgdata

I know EWW! 🙂 But it worked!

img data

That’s cool and all, but I bet what would be really cool is to open the file up in GIMP.

gimp

How’s that for coolness! It’s 12:18am EDT, I need to get to bed because I have to get up at 6:30am to get the kiddies ready for school. If all goes well, I should be able to get zmugfs 0.1 released by next week.

zmugfs shows image listings

Yesterday I started implementing the image listings for the albums. I finished it up today. Here are the latest screenshots.

First up is an ls listing of the Album which appears on the filesystem as a directory (click the image to see a larger view, and yes that is Obi-wan in the background). The permissions aren’t quite right for the images, but that’s a minor change to the stat structure.

zmugfs shows ls listing of images

Next up is a subcategory containing albums. If you recall before I didn’t show the image counts, now the image counts appear.

zmugfs shows image counts now

Finally, you can see an album with the list of images and their file sizes. Since this only returns the filename information nautilus can’t render the previews. Still this is cool stuff, well in my opinion it’s cool stuff 🙂

zmugfs image list

zmugfs via nautilus (part 2)

Almost a month ago I posted a screenshot of zmugfs via nautilus. Back then I was returning a hardcoded list of strings with no inode information. Last week, I got the read-only listing of categories and subcategories to work, see the ls output here.

I thought to myself, I bet folks would love to see the new read-only functionality through nautilus. Well, prepare to be amazed and bedazzled. 🙂 First up is a screenshot of a category showing all of the subcategories contained within. Notice all but one says “0 items”, that’s because I haven’t figured out how to get the image count for the albums contained within yet.

zmugfs category

The Birthday subcategory contains 9 items (all albums). So if you double click on that folder you are presented with a window showing all 9 albums in the subcategory.

zmugfs subcat

Isn’t that cool? You may be asking, “how do I know he’s not just returning some random list of strings?” Well, let’s look at smugmug to see what’s actually there:

actual smugmg subcategory

Next step is to get Albums to show their images and their image counts. Once that is done, I will release zmugfs 0.1 out in the wild. The following releases will add write support and possible performance enhancements.

zmugfs shows readonly (sub)categories

I kept fighting fuse.Direntry. When I tried:

for n in node.get_nodes():
     yield fuse.Direntry(n.path)

I would get NOTHING! but if I created a simple test list it would work:

f = ['foo', 'bar', 'path', 'cheese']
for i in f:
    yield fuse.Direntry(i)

It was confusing until I notice the only difference was that n.path was a unicode string while my test list were ascii stings. I thought to myself, “nah, that can’t be it”. But what do you know! I made this change:

yield fuse.Direntry(n.path.string('/').encode('ascii'))

Not sure if that will be the final code, IMO fuse.Direntry should work with unicode strings. But none the less, the result is a magnificent readonly view using ls and ls -l.

zmugfs shows ls

python’s default argument values

I’ve been trying to track down a bug for a couple days now with zmugfs. I’m caching the tree structure returned by smugmug.users.getTree. Here’s my Node class:

class Node(object):
    def __init__(self, path, inode=MyStat(), children=[]):
        self.path = path
        self.inode = inode
        self.children = children

To a newbie like me, this looks perfectly fine. If no inode is passed in create an instance of MyStat(), if no children are passed in create an empty list. Well guess what! That’s not how python works with lists. If you do what I did above the children list is shared! Imagine my horror when each Node has a reference to ALL of the children of the other Nodes. EEK! A few minutes of googling and I found this page:
4.7.1 Default Argument Values.

I changed the constructor of the Node class to be as follows:

def __init__(self, path, inode=MyStat(), children=None):
    self.path = path
    self.inode = inode
    if children is None:
        self.children = []
    else:
        self.children = children

This fixed my problem.

selfless isn’t always good

Last night I posted a little blurb about python’s self. My bud dgoodwin agreed with me, but after reading the reason it exists we both felt much better about having to use it. According to the General Python FAQ there’s a pretty good reason to have it:

The idea was borrowed from Modula-3. It turns out to be very useful, for a variety of reasons.

First, it’s more obvious that you are using a method or instance attribute instead of a local variable. Reading self.x or self.meth() makes it absolutely clear that an instance variable or method is used even if you don’t know the class definition by heart. In C++, you can sort of tell by the lack of a local variable declaration (assuming globals are rare or easily recognizable) — but in Python, there are no local variable declarations, so you’d have to look up the class definition to be sure. Some C++ and Java coding standards call for instance attributes to have an m_ prefix, so this explicitness is still useful in those languages, too.

Second, it means that no special syntax is necessary if you want to explicitly reference or call the method from a particular class. In C++, if you want to use a method from a base class which is overridden in a derived class, you have to use the :: operator — in Python you can write baseclass.methodname(self, ). This is particularly useful for __init__() methods, and in general in cases where a derived class method wants to extend the base class method of the same name and thus has to call the base class method somehow.

Finally, for instance variables it solves a syntactic problem with assignment: since local variables in Python are (by definition!) those variables to which a value assigned in a function body (and that aren’t explicitly declared global), there has to be some way to tell the interpreter that an assignment was meant to assign to an instance variable instead of to a local variable, and it should preferably be syntactic (for efficiency reasons). C++ does this through declarations, but Python doesn’t have declarations and it would be a pity having to introduce them just for this purpose. Using the explicit “self.var” solves this nicely. Similarly, for using instance variables, having to write “self.var” means that references to unqualified names inside a method don’t have to search the instance’s directories. To put it another way, local variables and instance variables live in two different namespaces, and you need to tell Python which namespace to use.

Hopefully you won’t curse python for being selfish next time.