5 Mar 2008 henrique   » (Observer)

I was implementing a distutils' setup script when I got a problem to define permissions to the so called data files. As I hadn't found anything in the documentation about this, the remaining alternative was to look at the source code (in distutils.command.install_data and distutils.cmd modules) to see how these things were expected to work.

For my surprise, permissions seemed not to be supported by distutils and all data files always were being installed in a non-restricted mode, i.e, 0777. So good, this liberal mode could be my solution, although I don't like the idea to be so limited in this way.

So the question was: although the mode 0777 was being used as default, the data files still being wrote with mode 0755 (actually, it depends on the umask of the user running the installer)... then, my last attempt was to look in depth to the remaining related modules and I found out that distutils implements its own "makedirs" on distutils.dir_utils.mkpath module and this function was ignoring completely its "mode" parameter (the one which defaults to 0777)! Finally I've found the culprit. I moved ahead and create a patch and report an issue on python's bug tracking system: http://bugs.python.org/issue2236.

Hmm, but the problem hadn't been fixed, even with mkpath using its mode parameter now, the files/directories were still being save with modes other than 0777; but the problem here is more complicated, it seems it is related to how python is using the mkdir system call: depending on the compiler directives, it doesn't specify the mode parameter to mkdir... I don't known why it is this way and I think we moved too down - the python core developers should have some good reason and it is out of the scope of this post).

So I go to the less attractive solution (IMO): to extend the distutils' install_data command. To do this we need to known a bit how the commands are structured and executed. The idea is quite simple, every distutils' command is a python module available through distutils.command dir. In this directory, there is a module for each command, and each module has a class with the same name, so we do:

from distutils.commands.install_data import install_data

class MyInstallData(install_data): pass

Each class must implement a method run(), which is the place to look to see how the command does its work. For the install_data command, the operations are closed to copy files and create directories (through copy_file() and mkpath() methods of the Command superclass). The mkpath() was the problem, so it is what needs to be extended:

class MyInstallData(install_data):
    def mkpath(self, name, mode=0777, verbose=0, dry_run=0):
        rv = Command.mkpath(self, name, mode, verbose, dry_run)
        os.chmod(name, mode) 
        return rv

When a path is created, I force the chmod to fix the permissions. Problem fixed. Not so good solution, but "it works" (tm). Ah! to use our custom install_data command with setup, we just specify the cmdclass parameter:

    name="package name",
    cmdclass={"install_data": MyInstallData,}

that's it.

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!