Patch for util.py (was: Re: [Distutils] bdist broken under Windows)

Greg Ward gward@python.net
Tue, 30 May 2000 22:17:33 -0400


On 30 May 2000, Thomas Heller said:
> What would the user do with such an archive when his python-site-packages
> are to be installed into "/usr/local/lib/python1.5/site-packages" ?

There's a reason these are called "dumb" built distributions.  They're
dumb!  Stupid, moronic, clue-challenged -- call 'em what you will
they're *dumb*.

However, 'change_root()' is definitely needed to support "bdist_rpm",
and may well be useful for other Unix build distributions.  In Unix,
/usr is always /usr, and /usr/local is always /usr/local -- and yes,
Python is not necessarily installed in either one of them.  However,
prefix is not necessarily always exec-prefix, so if we want to put the
whole dumb built distribution in one tarball, either we assume prefix ==
exec-prefix, or we start at the root.  Neither one is good enough, which
is why I don't expect dumb build distributions to catch on in a big way.

> Converting this into the windows world:
> 
> Normally python1.5 is installed into
> "C:\Program Files\Python".
> So the bdist-created archive would contain filenames like
> "C:\Program Files\Python\distutils\..."
> 
> On a german windows installation Python would be installed
> (per default) in
> "C:\Programme\Python"
> On the other hand, users would have been able to nstall it into
> "D:\Work\Python\1.5" or whatever they like.
> 
> So to me it seems like a bad idea to create archives containing pathnames
> relative to the root directory.

Good point.  Perhaps bdist_dumb should grow a *little* bit of
intelligence, and allow the packager to specify if the archive should be
relative to prefix or to the root.  Yeah, I like that.  Just added it to
the to-do list.

> Anyway, here is the relevant part of the implementation
> of change_root on windows:
> --------------------------------------
>     elif os.name == 'nt':
>         (root_drive, root_path) = os.path.splitdrive (new_root)
>         (drive, path) = os.path.splitdrive (pathname)
>         if path[0] == '\\':
>             path = path[1:]
>         return os.path.join (new_root, path)

Isn't splitdrive'ing new_root unnecessary here?  (Oh wait, you just
inherited that from my aborted attempt...)

Apart from that, this looks right to me... but I just enumerated 20
distinct cases that 'change_root()' has to handle with MS-DOS pathnames,
so you can be sure I'll be testing this one carefully.  ;-)

[...time passes...]

OK, I went ahead and wrote the test script.  Good thing too: I found a
bug in the Unix case!  Here's the output; first, the Unix cases:

ok: change_root(baz, foo) == baz/foo
ok: change_root(/baz, foo) == /baz/foo
ok: change_root(baz, /foo) == baz/foo
ok: change_root(/baz, /foo) == /baz/foo

and now the DOS/Windows cases:

ok: change_root(c:\baz, c:\foo) == c:\baz\foo
ok: change_root(\baz, c:\foo) == \baz\foo
ok: change_root(c:\baz, \foo) == c:\baz\foo
ok: change_root(\baz, \foo) == \baz\foo
not ok: change_root(c:\baz, c:foo) != c:\baz\foo (got c:\baz\c:foo)
not ok: change_root(\baz, c:foo) != c:\baz\foo (got \baz\c:foo)
ok: change_root(c:\baz, foo) == c:\baz\foo
ok: change_root(\baz, foo) == \baz\foo
ok: change_root(c:baz, c:\foo) == c:baz\foo
ok: change_root(baz, c:\foo) == baz\foo
ok: change_root(c:baz, \foo) == c:baz\foo
ok: change_root(baz, \foo) == baz\foo
not ok: change_root(c:baz, c:foo) != c:baz\foo (got c:baz\c:foo)
not ok: change_root(baz, c:foo) != baz\foo (got baz\c:foo)
ok: change_root(c:baz, foo) == c:baz\foo
ok: change_root(baz, foo) == baz\foo
ok: change_root(d:\baz, c:\foo) == d:\baz\foo
not ok: change_root(d:\baz, c:foo) != d:\baz\foo (got d:\baz\c:foo)
ok: change_root(d:baz, c:\foo) == d:baz\foo
not ok: change_root(d:baz, c:foo) != d:baz\foo (got d:baz\c:foo)

Hmmm, not quite there yet.

[...more time passes...]

OK, got it.  Here's the revised 'change_root()'.  Passes all those tests
for both POSIX and DOS/Windows paths.  Anyone care to contribute a Mac
OS version (and tests!)?

def change_root (new_root, pathname):
    """Return 'pathname' with 'new_root' prepended.  If 'pathname' is
    relative, this is equivalent to "os.path.join(new_root,pathname)".
    Otherwise, it requires making 'pathname' relative and then joining the
    two, which is tricky on DOS/Windows and Mac OS.
    """
    if os.name == 'posix':
        if not os.path.isabs (pathname):
            return os.path.join (new_root, pathname)
        else:
            return os.path.join (new_root, pathname[1:])

    elif os.name == 'nt':
        (drive, path) = os.path.splitdrive (pathname)
        if path[0] == '\\':
            path = path[1:]
        return os.path.join (new_root, path)

    elif os.name == 'mac':
        raise RuntimeError, "no clue how to do this on Mac OS"

    else:
        raise DistutilsPlatformError, \
              "nothing known about platform '%s'" % os.name

There, I've checked it in.  And the test script too!

> Another minor nit:
> distutils\archive_util.py, function make_zipfile: The (nested) function
> visit
> should normpath() the generated path names, otherwise the created zipfile
> contains pathnames like ".\distutils\archive_util.py" (Not nice at least).

OK, thanks -- I've checked in your fix.

> Question:
> Zip-files created under windows contain backslashes as path-separators.
> Do we have to care about this?

Shouldn't that be done magically by os.path.join()?

> PS: As soon as the above problems have been cleared, I will prepare
> a first release of bdist_wininst.py as discussed before.

Hot dog!

        Greg
-- 
Greg Ward - nerd                                        gward@python.net
http://starship.python.net/~gward/
I don't understand the HUMOUR of the THREE STOOGES!!