Archive

Author Archive

Death or beachball

May 29th, 2010

Pierre Igot’s post about Adobe’s use of a new cursor in CS5 draws attention to how Adobe is continuing to fail to adhere to Macintosh user interface conventions.

But I disagree that the correct cursor to use for a blocking task that cannot be cancelled is the spinning beachball of death. That cursor after all is the cursor automatically provided by the operating system when an application is not responding to input events. As such when I see the beachball I associate it with a stuck application, one that may need to be forcibly quit.

Previous versions of Photoshop showed the watch cursor for actions which took a significant length of time. Showing the watch cursor is friendlier than showing the beachball of death because it indicates that the application is busy, too busy to handle your clicks but everything is hunky dory and the train will arrive at the station.

There is no watch cursor listed in Apple’s documentation on cursors. However the cursor is still present in the system and can be found in the headers for the carbon appearance manager:

kThemeWatchCursor             = 7,    /* Can Animate */

If you have Xcode 3.2.2 installed you can find this in /Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Appearance.h, line 634.

I don’t know enough about Macintosh programming to say whether it is possible to employ this cursor from a Cocoa-based application.

david ,

More Python features that I really like

May 28th, 2010

Another thing that makes using Python pleasing is decorators. A decorator is a wrapper for a function (or method) that takes a function (or method) as an argument and returns a new function (or…) which is then bound to the name for the original function.

The newly-decorated function can then do things like checking the called arguments before invoking the original un-decorated function.

Django provides decorators for authentication so that you can wrap a view function with a check for client credentials before deciding whether to return the original response or a deny access.

In this manner Django’s authentication decorators encourage orthogonal code: the logic for displaying a view is separated from the logic for deciding whether you should be permitted to see the view’s output. By keeping them separate, it becomes simpler to re-use the authentication logic and apply it to other views.

Suppose you have a view that accepts a Django request object and checks whether the user is signed in:

def administration_page(request):
    if request.user.is_authenticated():
        return HttpResponse("Welcome, dear user.")
    else:
        return HttpResponseRedirect("/signin/")

With a decorator you can simplify and clarify things:

@login_required
def administration_page(request):
    return HttpResponse("Welcome, dear user.")

For older versions of Python (pre 2.4) which don’t understand the @ operator one must explicitly decorate the view function like so:

def administration_page(request):
    return HttpResponse("Welcome, dear administrator.")

administration_page = login_required(administration_page)

Note in the example that the original administration_page function is passed to the decorator. The @ syntax in the first example makes that implicit but the two are equivalent.

The implementation of a decorator is interesting. It takes the function itself as an argument and returns a new function which does the actual checking. Here is how the decorator used above might do its stuff:

def login_required(view_function):
    def decorated_function(request):
        if request.user.is_authenticated():
            return view_function(request)
        else:
            return HttpResponseRedirect("/signin/")

    return decorated_function

The actual implementation of Django’s login_required decorator is considerably less idiotic. Python’s functools module has helpers for writing well-behaved decorators.

Because functions in Python are themselves objects the decorator can accept a function reference, construct a new function that checks for authentication and then return a reference to that new function.

Simples!

(Simples gets less simples when you want to write a decorator that accepts configuration arguments because you then need either another layer of nested function definitions or a class whose instances can be called directly, but I’m going to ignore you for a bit and wow is that Concorde…?)

david , ,

Nginx and Wordpress

May 1st, 2010

My Nginx and Wordpress configuration on Debian Linux 5 (Lenny). This has Nginx as the Web server using the fastcgi module to talk to php-cgi processes that run Wordpress with pretty URLs.

The virtual private server I installed this on (from John Companies) has a 256 megabyte slice, so I figured a regular Apache + mod_php setup might be in trouble seeing as one of the sites I am running gets several thousand visits a day. Up to now I have always used Apache with mod_php to run Wordpress, and anyway it is fun to learn how unfamiliar software works (Lotus Notes excepted).

On a side note, SysV run-levels and /etc/rcX.d directories are needlessly clever. sysv-rc-conf makes editing those easy.

server {
    listen   80;
    server_name  example.com;

    root   /home/david/example.com;
    index  index.php index.html index.htm;
    error_page   500 502 503 504  /50x.html;

    location / {

    }

    location = /50x.html {
        root   /var/www/nginx-default;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    location ~ /\.ht {
        deny  all;
    }

    location /b/ {
        if (!-e $request_filename) {
            rewrite ^(.+)$ /b/index.php?q=$1 last;
        }
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

# Rewrite www.example.com to example.com
server {
    listen 80;
    server_name www.example.com;
    rewrite ^ http://example.com$request_uri?;
}

This configuration is for Wordpress installed under http://example.com/b/ on my server.

david , ,

Split a file on any character in Python

April 15th, 2010

I need to split a big text file on a certain character. I expect I am being thick about this, but split doesn’t quite do what I want because it includes the matching line, whereas I want to split right on the matching character.

My Python answer:

def readlines(filename, endings, chunksize=4096):
    """Returns a generator that splits on lines in a file with the given
    line-ending.
    """
    line = ''
    while True:        
        buf = filename.read(chunksize)
        if not buf:
            yield line
            break

        line = line + buf

        while endings in line:
            idx = line.index(endings) + len(endings)
            yield line[:idx]
            line = line[idx:]

if __name__ == "__main__":
    import sys, os

    FORMFEED = chr(12) # ASCII 12
    basename = os.path.basename(sys.argv[1])
    for num, data in enumerate(readlines(open(sys.argv[1]), endings=FORMFEED)):
        filename = basename + '-' + str(num)
        open(filename, 'wb').write(data)

This is also useful when reading data exported from some old-fashioned Mac application like Filemaker 5 where the line-endings are ASCII 13 not ASCII 10.

This post was inspired by Lotus Notes version 8.5, which is so advanced that to save a message in a file on disk you have to export it as structured text. And if you want to save a whole bunch of messages as individual files you must forget that drag-and-drop was introduced with System 7, that would be too obvious.

david ,

Django AdminForm objects and templates

April 4th, 2010

I can’t find documentation for the context of a Django admin template. In particular, where is the form and how does one access the fields? This post describes the template context for a generic admin model for Django 1.1.

Django uses an instance of ModelAdmin (defined in django.contrib.admin.options) to handle the request for a model object add / change view in the admin site. ModelAdmin.add_view and ModelAdmin.change_view are responsible for populating the template context when rendering the add object and change object pages respectively.

Here are the keys common to add and change views:

  • title, ‘Add ‘ or ‘Change ‘ + your model class’ _meta.verbose_name
  • adminform is an instance of AdminForm
  • is_popup, a boolean which is true when _popup is passed as a request parameter
  • media is an instance of django.forms.Media
  • inline_admin_formsets is a list of InlineAdminFormSet objects
  • errors is an instance of AdminErrorList
  • root_path is the root_path attribute of the AdminSite object
  • app_label is your model class’ _meta.app_label attribute

The way that Django renders a form in the admin view is to iterate over the adminform instance and then iterate over each FieldSet which in turn yield AdminField instances. All I want to do is layout the form fields, ignoring the fieldset groupings which may or may not be defined in the model’s ModelAdmin.fieldset attribute.

This turns out to be easy once you know how. The regular form is an attribute of the adminform object. So if your model has a field named “king_of_pop” you can refer to the form field in your template like so:

{{ adminform.form.king_of_pop.label_tag }}: {{ adminform.form.king_of_pop }}

Or if you want to save your finger tips you can use the with template tag:

{% with adminform.form as f %}
{{ f.king_of_pop.label_tag }}: {{ f.king_of_pop }}
{% endwith %}

Delving through the Django source while I tried to understand all of this I was struck by how Python defines hook functions for iteration and accessing attributes. Half of Python’s attraction is in how easy it is from the program author’s point of view to treat objects as built-in types like lists, dicts, etc.; the other half is the responsibility of the author of a Python module to encourage that same ease of use by implementing the related iteration protocols. It is harder to write a good Python module than it is to write a good Python program that uses a good module.

david ,

Using MacPorts behind a firewall

March 31st, 2010

I failed to persuade MySQLdb to build on a Mac OS X Server 10.5.8 install using the system Python + MySQL installation. So I turned to MacPorts where I know I can get Django + all the bits working without much hassle (but with much patience).

The next problem was that MacPorts couldn’t update because rsync was blocked by the corporate access policy. Fortunately plain HTTP is permitted outbound. Here’s how to use a local ports tree.

Install MacPorts using the disk image for 10.5.

curl -O http://distfiles.macports.org/MacPorts/MacPorts-1.8.2-10.5-Leopard.dmg
hdiutil attach MacPorts-1.8.2-10.5-Leopard.dmg
sudo installer -pkg /Volumes/MacPorts-1.8.2/MacPorts-1.8.2.pkg -target /
hdiutil detach /Volumes/MacPorts-1.8.2

If the MacPorts install directories are not in your $PATH environment, you can add them to your .profile. This change will not take effect until you start a new terminal session.

cat >> ~/.profile <<EOF
PATH=/opt/local/bin:/opt/local/sbin:${PATH}
MANPATH=/opt/local/share/man:${MANPATH}
EOF

After you have installed MacPorts, create a directory for the ports tree and check it out using Subversion.

sudo mkdir -p /opt/local/var/macports/sources/svn.macports.org/trunk/dports
cd /opt/local/var/macports/sources/svn.macports.org/trunk/dports
sudo svn co http://svn.macports.org/repository/macports/trunk/dports/ .

N.B. In the last line beginning svn co ... the trailing directory separator is significant!

Now tell MacPorts to use the local checkout rather than rsync. Edit /opt/local/etc/macports/sources.conf and add a new line to the end with the path to the ports tree, then comment out the previous line that uses rsync. Here are the last lines from my configuration:

#rsync://rsync.macports.org/release/ports/ [default]
file:///opt/local/var/macports/sources/svn.macports.org/trunk/dports/ [default]

Finally you must create an index for the tree (otherwise you will see messages saying “Warning: No index(es) found!”).

cd /opt/local/var/macports/sources/svn.macports.org/trunk/dports
sudo portindex

Now go do great things.

david , ,

Confused of Wapping

March 28th, 2010

Trying to get my head straight about what it means to modify a file and what it means to modify a folder for a desktop operating system. Mac OS X’s behaviour feels intuitively wrong, but turns out it is much harder than I expected to nail down exactly why it is wrong.

In general the implementation of filesystem metadata on OS X has been two steps back with respect to the old Mac OS ways. (The one step forward has been the rich metadata provided by the Finder in terms of previews, performance on folders with hundreds of files, and the exposing of file content metadata with the interface for finding files.)

I feel that Mac OS X is shifting away from the concept of files existing on a filesystem without providing a suitable alternative.

david

watchedinstall is useful

January 30th, 2010

Very satisfying to use watchedinstall at work the other day to see exactly what a tricksy meta-package was doing during installation. Now that I fixed a stupid bug involving dtrace, watchedinstall works a treat for recording exactly what goes where.

Many thanks to Preston Holmes for releasing watchedinstall in the first place.

My goal is to replace the functionality of the fsevents helper application with a dtrace script that can list filesystem changes. A single python script would be simpler to install and use – you wouldn’t need to install it at all, just run it from the directory you downloaded it to. No effing about with setting PATH environment variables, no worry about compiling a C program for whatever architecture.

Hey Esther!

david ,

AJAX-ified result paging is not good

January 30th, 2010

An annoying trend in Web design: using AJAX to load results when there is more than one page.

Apple does this for their search results. Netgear does this when searching their knowledge base. Microsoft does this for their Mactopia discussion forums. All three ostensibly good, clean designs fail to consider what the hell a visitor wants in the first place, which is to see the next damn page of results.

The first problem with using AJAX to load results is that the browser view does not change when the new results are loaded. Suppose you have read the first ten results, you scroll to the last result on the page and the first result scrolls up and out of view. Then you click the link for the next page of results. The fancy AJAX loader replaces the existing list of results with the next page’s list of results, but does not move the view, leaving you staring at the last result on the second page when what you want is to see the first result of the second page, so you have to scroll back to the top of the page.

The script to load the results should scroll the view so the first result of the subsequent page is visible – I have yet to see an example of this behaviour.

The second problem is the URL does not change between one page and the next, which means you cannot bookmark any page other than the first. URLs and hyperlinks are the very stuff of the Web, it is mad not to make use of them.

My guess is that the Web designer in each of these cases was so pleased by the effect of updating the visitor’s view of a page without changing the browser location that she figured it was an improvement over the established technique of passing query parameters in a URL.

It is not. Please go back to the old-fashioned use of a query parameter to indicate the offset into a list of results.

david

Tea Timer widget

January 30th, 2010

Finally found a use for Dashboard with Stefan Scherfke’s Tea Timer.

david ,