[Python-3000] Mini Path object

Mike Orr sluggoster at gmail.com
Wed Nov 8 22:21:05 CET 2006


On 11/8/06, Talin <talin at acm.org> wrote:
> What I want is a way to have data files that contain embedded paths
> encoded in a way that allows those data files to be transported across
> platform. Generally those embedded paths will be relative to something,
> rather than fully-qualfied, such as ${root}/something/or/other.
>
> What I want to avoid is a situation where I have to edit my config file
> to switch all the path separators from '/' to '\' when I move my
> application from OS X to Win32.
>
> As a Python programmer, my ideal is to be able to write as if I *didn't
> know* what the underlying platform was.

I think my proposal yesterday will do it for you.

    p = Path(  PosixPath("something/or/other")  )

This would convert a Posix relative path to the native format if
different, using .components as a universal intermediary.

I'm inclined to raise an exception if the user tries to convert from
an absolute path since there's no one obvious way to do it.  Let the
user make the path relative and then explicitly attach it to a new
base directory.

> I think that there's a reasonable chance of acceptance for an object
> that does filesystem-like operations that *doesn't overlap* with what
> the Path object does. But what you are proposing is a *superset* of what
> Path does (because you're saying its a subclass), and I don't think that
> will go over well.

It's a "contains" relationship, not a subclass.  FSPath contains a
path in its .path attribute.

Path(object):
    .pathlib = os.path        # Class attribute.

PosixPath(Path):
    .pathlib = posixpath

FSPath(object):
    .path = Path("foo")       # Instance attribute.

FSPath will call the Path constructor whenever it needs to create a
path object.  That's the only relationship between the two.  Three
occasions have been mentioned:

    - When FSPath() is called with any arguments other than a single
Path instance.  It passes the arguments to Path() and sets self.path
to the result.
    - FSPath.cwd() does 'return FSPath(   Path(os.getcwd())   )'.
    - If .norm and .expand are moved to FSPath, they will return new
FSPath objects initialized to new Path objects.

(Previously I said you could make FSPath use an alternate *Path class.
On reflection that's unnecessary, since FSPath can't do anything with
a non-native path anyway.)

I would personally like Path and FSPath combined into one class, so
this is a compromise on my part.  The argument for a "pure path
algebra" class seems academic: who really needs it?  (Talin's
cross-platform example could be handled by a conversion class designed
for that purpose.)  os.path mixes FS-independent and FS-dependent
functions together, and I've never heard anybody say that's a problem.
 So why is it suddenly a problem now?  We've seen that path joining,
the current directory, absolute paths, normalization, and FS-sensitive
".." all imply an interaction between Path and FSPath, so it's a messy
split.

I'm thinking about proposing .absolute() and .relative() to replace
.aspath(), where .absolute() prefixes the cwd and .relative() chops
off the root only.  Although with the Path/FSPath distinction,
.absolute() would have to go on FSPath and .relative() belongs on
Path.  Users might wonder why the two aren't together.

> > People need to add/delete/replace extensions, and they don't want to
> > use character slicing / .rfind / .endswith / len() / + to do it.  They
> > expect the library to at least handle extension splitting as well as
> > os.path does.  Adding a few convenience methods would be unobtrusive
> > and express people really want to do:
> >
> >     p2 = p.add_ext(".tar")
> >     p2 = p.del_ext()
> >     p2 = Path("foo.gzip").replace_ext(".bz2")
> >
> > But what harm is there in making them scalable to multiple extensions?
> >
> >     .add_exts(*exts)
> >     .del_exts(N)
> >     .replace_exts(N, *exts)
>
> Someone in another message pointed out that paths, being based on
> strings, are immutable, so this whole handling of extensions will have
> to be done another way.

They would return new Path's, just like str.replace() does.  I do want
them to be dictionary keys so they'd have to be immutable.  I'm
veering toward this syntax now:

    .add_ext(*exts)    # Mainly for one but can unobtrusively add more.
    .del_ext(N=1)       # Mainly for one but can unobtrusively delete more.
    .replace_ext(ext)    # Replace one extension only (e.g., ".jpg" to ".gif").

-- 
Mike Orr <sluggoster at gmail.com>


More information about the Python-3000 mailing list