Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 355 – Path - Object oriented filesystem paths

Author:
Björn Lindqvist <bjourne at gmail.com>
Status:
Rejected
Type:
Standards Track
Created:
24-Jan-2006
Python-Version:
2.5
Post-History:


Table of Contents

Rejection Notice

This PEP has been rejected (in this form). The proposed path classis the ultimate kitchen sink; but the notion that it’s better toimplementall functionality that uses a path as a method on a singleclass is an anti-pattern. (E.g. why notopen()? Orexecfile()?)Subclassing from str is a particularly bad idea; many stringoperations make no sense when applied to a path. This PEP haslingered, and while the discussion flares up from time to time,it’s time to put this PEP out of its misery. A less far-fetchedproposal might be more palatable.

Abstract

This PEP describes a new class,Path, to be added to theosmodule, for handling paths in an object oriented fashion. The“weak” deprecation of various related functions is also discussedand recommended.

Background

The ideas expressed in this PEP are not recent, but have beendebated in the Python community for many years. Many have feltthat the API for manipulating file paths as offered in the os.pathmodule is inadequate. The first proposal for aPath object wasraised by Just van Rossum on python-dev in 2001[2]. In 2003,Jason Orendorff released version 1.0 of the “path module” whichwas the first public implementation that used objects to representpaths[3].

The path module quickly became very popular and numerous attemptswere made to get the path module included in the Python standardlibrary;[4],[5],[6],[7].

This PEP summarizes the ideas and suggestions people haveexpressed about the path module and proposes that a modifiedversion should be included in the standard library.

Motivation

Dealing with filesystem paths is a common task in any programminglanguage, and very common in a high-level language like Python.Good support for this task is needed, because:

  • Almost every program uses paths to access files. It makes sensethat a task, that is so often performed, should be as intuitiveand as easy to perform as possible.
  • It makes Python an even better replacement language forover-complicated shell scripts.

Currently, Python has a large number of different functionsscattered over half a dozen modules for handling paths. Thismakes it hard for newbies and experienced developers to choosethe right method.

ThePath class provides the following enhancements over thecurrent common practice:

  • One “unified” object provides all functionality from previousfunctions.
  • Subclassability - thePath object can be extended to supportpaths other than filesystem paths. The programmer does not needto learn a new API, but can reuse their knowledge of Pathto deal with the extended class.
  • With all related functionality in one place, the right approachis easier to learn as one does not have to hunt through manydifferent modules for the right functions.
  • Python is an object oriented language. Just like files,datetimes and sockets are objects so are paths, they are notmerely strings to be passed to functions.Path objects isinherently a pythonic idea.
  • Path takes advantage of properties. Properties make for morereadable code:
    ifimgpath.ext=='jpg':jpegdecode(imgpath)

    Is better than:

    ifos.path.splitexit(imgpath)[1]=='jpg':jpegdecode(imgpath)

Rationale

The following points summarize the design:

  • Path extends from string, therefore all code which expectsstring pathnames need not be modified and no existing code willbreak.
  • APath object can be created either by using the classmethodPath.cwd, by instantiating the class with a string representinga path or by using the default constructor which is equivalenttoPath(".").
  • Path provides common pathname manipulation, pattern expansion,pattern matching and other high-level file operations includingcopying. BasicallyPath provides everything path-related exceptthe manipulation of file contents, for which file objects arebetter suited.
  • Platform incompatibilities are dealt with by not instantiatingsystem specific methods.

Specification

This class defines the following public interface (docstrings havebeen extracted from the reference implementation, and shortenedfor brevity; see the reference implementation for more detail):

classPath(str):# Special Python methods:def__new__(cls,*args)=>Path"""        Creates a new path object concatenating the *args.  *args        may only contain Path objects or strings.  If *args is        empty, Path(os.curdir) is created.        """def__repr__(self):...def__add__(self,more):...def__radd__(self,other):...# Alternative constructor.defcwd(cls):...# Operations on path strings:defabspath(self)=>Path"""Returns the absolute path of self as a new Path object."""defnormcase(self):...defnormpath(self):...defrealpath(self):...defexpanduser(self):...defexpandvars(self):...defbasename(self):...defexpand(self):...defsplitpath(self)=>(Path,str)"""p.splitpath() -> Return (p.parent, p.name)."""defstripext(self)=>Path"""p.stripext() -> Remove one file extension from the path."""defsplitunc(self):...# See footnote [1]defsplitall(self):...defrelpath(self):...defrelpathto(self,dest):...# Properties about the path:parent=>Path"""This Path's parent directory as a new path object."""name=>str"""The name of this file or directory without the full path."""ext=>str"""        The file extension or an empty string if Path refers to a        file without an extension or a directory.        """drive=>str"""        The drive specifier.  Always empty on systems that don't        use drive specifiers.        """namebase=>str"""        The same as path.name, but with one file extension        stripped off.        """uncshare[1]# Operations that return lists of paths:deflistdir(self,pattern=None):...defdirs(self,pattern=None):...deffiles(self,pattern=None):...defwalk(self,pattern=None):...defwalkdirs(self,pattern=None):...defwalkfiles(self,pattern=None):...defmatch(self,pattern)=>bool"""Returns True if self.name matches the given pattern."""defmatchcase(self,pattern)=>bool"""        Like match() but is guaranteed to be case sensitive even        on platforms with case insensitive filesystems.        """defglob(self,pattern):# Methods for retrieving information about the filesystem# path:defexists(self):...defisabs(self):...defisdir(self):...defisfile(self):...defislink(self):...defismount(self):...defsamefile(self,other):...# See footnote [1]defatime(self):..."""Last access time of the file."""defmtime(self):..."""Last-modified time of the file."""defctime(self):..."""        Return the system's ctime which, on some systems (like        Unix) is the time of the last change, and, on others (like        Windows), is the creation time for path.        """defsize(self):...defaccess(self,mode):...# See footnote [1]defstat(self):...deflstat(self):...defstatvfs(self):...# See footnote [1]defpathconf(self,name):...# See footnote [1]# Methods for manipulating information about the filesystem# path.defutime(self,times)=>Nonedefchmod(self,mode)=>Nonedefchown(self,uid,gid)=>None# See footnote [1]defrename(self,new)=>Nonedefrenames(self,new)=>None# Create/delete operations on directoriesdefmkdir(self,mode=0777):...defmakedirs(self,mode=0777):...defrmdir(self):...defremovedirs(self):...# Modifying operations on filesdeftouch(self):...defremove(self):...defunlink(self):...# Modifying operations on linksdeflink(self,newpath):...defsymlink(self,newlink):...defreadlink(self):...defreadlinkabs(self):...# High-level functions from shutildefcopyfile(self,dst):...defcopymode(self,dst):...defcopystat(self,dst):...defcopy(self,dst):...defcopy2(self,dst):...defcopytree(self,dst,symlinks=True):...defmove(self,dst):...defrmtree(self,ignore_errors=False,onerror=None):...# Special stuff from osdefchroot(self):...# See footnote [1]defstartfile(self):...# See footnote [1]

Replacing older functions with the Path class

In this section, “a ==> b” means that b can be used as areplacement for a.

In the following examples, we assume that thePath class isimported withfrompathimportPath.

  • Replacingos.path.join:
    os.path.join(os.getcwd(),"foobar")==>Path(Path.cwd(),"foobar")os.path.join("foo","bar","baz")==>Path("foo","bar","baz")
  • Replacingos.path.splitext:
    fname="Python2.4.tar.gz"os.path.splitext(fname)[1]==>fname=Path("Python2.4.tar.gz")fname.ext

    Or if you want both parts:

    fname="Python2.4.tar.gz"base,ext=os.path.splitext(fname)==>fname=Path("Python2.4.tar.gz")base,ext=fname.namebase,fname.extx
  • Replacingglob.glob:
    lib_dir="/lib"libs=glob.glob(os.path.join(lib_dir,"*s.o"))==>lib_dir=Path("/lib")libs=lib_dir.files("*.so")

Deprecations

Introducing this module to the standard library introduces a needfor the “weak” deprecation of a number of existing modules andfunctions. These modules and functions are so widely used thatthey cannot be truly deprecated, as in generatingDeprecationWarning. Here “weak deprecation” means notes in thedocumentation only.

The table below lists the existing functionality that should bedeprecated.

Path method/propertyDeprecates function
normcase()os.path.normcase()
normpath()os.path.normpath()
realpath()os.path.realpath()
expanduser()os.path.expanduser()
expandvars()os.path.expandvars()
parentos.path.dirname()
nameos.path.basename()
splitpath()os.path.split()
driveos.path.splitdrive()
extos.path.splitext()
splitunc()os.path.splitunc()
__new__()os.path.join(), os.curdir
listdir()os.listdir() [fnmatch.filter()]
match()fnmatch.fnmatch()
matchcase()fnmatch.fnmatchcase()
glob()glob.glob()
exists()os.path.exists()
isabs()os.path.isabs()
isdir()os.path.isdir()
isfile()os.path.isfile()
islink()os.path.islink()
ismount()os.path.ismount()
samefile()os.path.samefile()
atime()os.path.getatime()
ctime()os.path.getctime()
mtime()os.path.getmtime()
size()os.path.getsize()
cwd()os.getcwd()
access()os.access()
stat()os.stat()
lstat()os.lstat()
statvfs()os.statvfs()
pathconf()os.pathconf()
utime()os.utime()
chmod()os.chmod()
chown()os.chown()
rename()os.rename()
renames()os.renames()
mkdir()os.mkdir()
makedirs()os.makedirs()
rmdir()os.rmdir()
removedirs()os.removedirs()
remove()os.remove()
unlink()os.unlink()
link()os.link()
symlink()os.symlink()
readlink()os.readlink()
chroot()os.chroot()
startfile()os.startfile()
copyfile()shutil.copyfile()
copymode()shutil.copymode()
copystat()shutil.copystat()
copy()shutil.copy()
copy2()shutil.copy2()
copytree()shutil.copytree()
move()shutil.move()
rmtree()shutil.rmtree()

ThePath class deprecates the whole ofos.path,shutil,fnmatchandglob. A big chunk ofos is also deprecated.

Closed Issues

A number contentious issues have been resolved since this PEPfirst appeared on python-dev:

  • The__div__() method was removed. Overloading the / (division)operator may be “too much magic” and make path concatenationappear to be division. The method can always be re-added laterif the BDFL so desires. In its place,__new__() got an*argsargument that accepts bothPath and string objects. The*argsare concatenated withos.path.join() which is used to constructthePath object. These changes obsoleted the problematicjoinpath() method which was removed.
  • The methods and the propertiesgetatime()/atime,getctime()/ctime,getmtime()/mtime andgetsize()/size duplicatedeach other. These methods and properties have been merged toatime(),ctime(),mtime() andsize(). The reason they are notproperties instead, is because there is a possibility that theymay change unexpectedly. The following example is notguaranteed to always pass the assertion:
    p=Path("foobar")s=p.size()assertp.size()==s

Open Issues

Some functionality of Jason Orendorff’s path module have beenomitted:

  • Function for opening a path - better handled by the builtinopen().
  • Functions for reading and writing whole files - better handledby file objects’ ownread() andwrite() methods.
  • Achdir() function may be a worthy inclusion.
  • A deprecation schedule needs to be set up. How muchfunctionality shouldPath implement? How much of existingfunctionality should it deprecate and when?
  • The name obviously has to be either “path” or “Path,” but whereshould it live? In its own module or inos?
  • Due toPath subclassing eitherstr orunicode, the followingnon-magic, public methods are available onPath objects:
    capitalize(),center(),count(),decode(),encode(),endswith(),expandtabs(),find(),index(),isalnum(),isalpha(),isdigit(),islower(),isspace(),istitle(),isupper(),join(),ljust(),lower(),lstrip(),replace(),rfind(),rindex(),rjust(),rsplit(),rstrip(),split(),splitlines(),startswith(),strip(),swapcase(),title(),translate(),upper(),zfill()

    On python-dev it has been argued whether this inheritance issane or not. Most persons debating said that most stringmethods doesn’t make sense in the context of filesystem paths –they are just dead weight. The other position, also argued onpython-dev, is that inheriting from string is very convenientbecause it allows code to “just work” withPath objects withouthaving to be adapted for them.

    One of the problems is that at the Python level, there is no wayto make an object “string-like enough,” so that it can be passedto the builtin functionopen() (and other builtins expecting astring or buffer), unless the object inherits from eitherstr orunicode. Therefore, to not inherit from string requires changesin CPython’s core.

The functions and modules that this new module is trying toreplace (os.path,shutil,fnmatch,glob and parts ofos) areexpected to be available in future Python versions for a longtime, to preserve backwards compatibility.

Reference Implementation

Currently, thePath class is implemented as a thin wrapper aroundthe standard library modulesfnmatch,glob,os,os.path andshutil. The intention of this PEP is to move functionality fromthe aforementioned modules toPath while they are beingdeprecated.

For more detail and an implementation see:

http://wiki.python.org/moin/PathModule

Examples

In this section, “a ==> b” means that b can be used as areplacement for a.

  • Make all python files in the a directory executable:
    DIR='/usr/home/guido/bin'forfinos.listdir(DIR):iff.endswith('.py'):path=os.path.join(DIR,f)os.chmod(path,0755)==>forfinPath('/usr/home/guido/bin').files("*.py"):f.chmod(0755)
  • Delete emacs backup files:
    defdelete_backups(arg,dirname,names):fornameinnames:ifname.endswith('~'):os.remove(os.path.join(dirname,name))os.path.walk(os.environ['HOME'],delete_backups,None)==>d=Path(os.environ['HOME'])forfind.walkfiles('*~'):f.remove()
  • Finding the relative path to a file:
    b=Path('/users/peter/')a=Path('/users/peter/synergy/tiki.txt')a.relpathto(b)
  • Splitting a path into directory and filename:
    os.path.split("/path/to/foo/bar.txt")==>Path("/path/to/foo/bar.txt").splitpath()
  • List all Python scripts in the current directory tree:
    list(Path().walkfiles("*.py"))

References and Footnotes

[1] Method is not guaranteed to be available on all platforms.

[2]
“(idea) subclassable string: path object?”, van Rossum, 2001https://mail.python.org/pipermail/python-dev/2001-August/016663.html
[3]
“path module v1.0 released”, Orendorff, 2003https://mail.python.org/pipermail/python-announce-list/2003-January/001984.html
[4]
“Some RFE for review”, Birkenfeld, 2005https://mail.python.org/pipermail/python-dev/2005-June/054438.html
[5]
“path module”, Orendorff, 2003https://mail.python.org/pipermail/python-list/2003-July/174289.html
[6]
“PRE-PEP: new Path class”, Roth, 2004https://mail.python.org/pipermail/python-list/2004-January/201672.html
[7]
http://wiki.python.org/moin/PathClass

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0355.rst

Last modified:2025-02-01 08:59:27 GMT


[8]ページ先頭

©2009-2025 Movatter.jp