Unipath
An object-oriented approach to Python file/directory operations.
Install / Use
/learn @mikeorr/UnipathREADME
Unipath %%%%%%%
An object-oriented approach to file/directory operations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
:Version: 1.1 :Home page: https://github.com/mikeorr/Unipath :Docs: https://github.com/mikeorr/Unipath#readme :Author: Mike Orr sluggoster@gmail.com :License: MIT (http://opensource.org/licenses/MIT)
.. To format this document as HTML: rst2html.py README.txt README.html
Unipath is an object-oriented front end to the file/directory functions scattered throughout several Python library modules. It's based on Jason Orendorff's path.py but focuses more on user convenience rather than on strict adherence to the underlying functions' syntax. Unipath is stable, well-tested, and has been used in production since 2008. It runs on Python 2.6+ and 3.2+.
Version 1.1 is a bugfix release. It fixes a Unicode incompatibility on Python 3 under Windows (or operating systems with native unicode filenames). The license is changed to MIT. It's as permissive as the former Python license but is smaller and simpler to read.
Python 3.4 introduced another object-oriented path library, pathlib. It's
available on PyPI as pathlib2 for older versions of Python. (pathlib on
PyPI is a frozen earlier version.) Unipath is now in maintenance mode. The
author is exploring a subclass of pathlib(2) adding some of Unipath's features.
.. contents::
Introduction
The Path class encapsulates the file/directory operations in Python's
os, os.path, and shutil modules. (Non-filesystem operations are in
the AbstractPath superclass, but users can ignore this.)
The API has been streamlined to focus on what the application developer wants
to do rather than on the lowest-level operations; e.g., .mkdir() succeeds
silently if the directory already exists, and .rmtree() doesn't barf if the
target is a file or doesn't exist. This allows the developer to write simple
calls that "just work" rather than entire if-stanzas to handle low-level
details s/he doesn't care about. This makes applications more self-documenting
and less cluttered.
Convenience methods:
.read_fileand.write_fileencapsulate the open/read/close pattern..needs_update(others)tells whether the path needs updating; i.e., if it doesn't exist or is older than any of the other paths..ancestor(N)returns the Nth parent directory, useful for joining paths..child(\*components)is a "safe" version of join..split_root()handles slash/drive/UNC absolute paths in a uniform way.
Sample usage for pathname manipulation::
>>> from unipath import Path
>>> p = Path("/usr/lib/python2.5/gopherlib.py")
>>> p.parent
Path("/usr/lib/python2.5")
>>> p.name
Path("gopherlib.py")
>>> p.ext
'.py'
>>> p.stem
Path('gopherlib')
>>> q = Path(p.parent, p.stem + p.ext)
>>> q
Path('/usr/lib/python2.5/gopherlib.py')
>>> q == p
True
Sample usage for filesystem access::
>>> import tempfile
>>> from unipath import Path
>>> d = Path(tempfile.mkdtemp())
>>> d.isdir()
True
>>> p = Path(d, "sample.txt")
>>> p.exists()
False
>>> p.write_file("The king is a fink!")
>>> p.exists()
True
>>> print(p.read_file())
The king is a fink!
>>> d.rmtree()
>>> p.exists()
False
Path objects subclass str (Python 2 unicode), so they can be passed
directly to fuctions expecting a string path. They are also immutable and can
be used as dictionary keys.
The name "Unipath" is short for "universal path". It was originally intended to
unify the competing path APIs as of PEP 334. When the PEP was rejected, Unipath
added some convenience APIs. The code is implemented in layers, with
filesystem-dependent code in the Path class and filesystem-independent code
in its AbstractPath superclass.
Installation and testing
Run "pip install Unipath". Or to install the development version, check out the source from the Git repository above and run "python setup.py develop".
To test the library, install 'pytest' and run "pytest test.py". It also comes with a Tox INI file.
Path and AbstractPath objects
Constructor
Path (and AbstractPath) objects can be created from a string path, or
from several string arguments which are joined together a la os.path.join.
Each argument can be a string, an (Abstract)Path instance, an int or long,
or a list/tuple of strings to be joined::
p = Path("foo/bar.py") # A relative path
p = Path("foo", "bar.py") # Same as previous
p = Path(["foo", "bar.py"]) # Same as previous
p = Path("/foo", "bar", "baz.py") # An absolute path: /foo/bar/baz.py
p = Path("/foo", Path("bar/baz.py")) # Same as previous
p = Path("/foo", ["", "bar", "baz.py"]) # Embedded Path.components() result
p = Path("record", 123) # Same as Path("record/123")
p = Path("") # An empty path
p = Path() # Same as Path(os.curdir)
To get the actual current directory, use Path.cwd(). (This doesn't work
with AbstractPath, of course.
Adding two paths results in a concatenated path. The other string methods
return strings, so you'll have to wrap them in Path to make them paths
again. A future version will probably override these methods to return paths.
Multiplying a path returns a string, as if you'd ever want to do that.
Normalization
The new path is normalized to clean up redundant ".." and "." in the
middle, double slashes, wrong-direction slashes, etc. On
case-insensitive filesystems it also converts uppercase to lowercase.
This is all done via os.path.normpath(). Here are some examples
of normalizations::
a//b => a/b
a/../b => b
a/./b => a/b
a/b => a\\b # On NT.
a\\b.JPG => a\\b.jpg # On NT.
If the actual filesystem path contains symbolic links, normalizing ".." goes to
the parent of the symbolic link rather than to the parent of the linked-to
file. For this reason, and because there may be other cases where normalizing
produces the wrong path, you can disable automatic normalization by setting the
.auto_norm class attribute to false. I'm not sure whether Unipath should
normalize by default, so if you care one way or the other you should explicitly
set it at the beginning of your application. You can override the auto_norm
setting by passing "norm=True" or "norm=False" as a keyword argument to the
constructor. You can also call .norm() anytime to manually normalize the
path.
Properties
Path objects have the following properties:
.parent The path without the final component. .name The final component only. .ext The last part of the final component beginning with a dot (e.g., ".gz"), or "" if there is no dot. This is also known as the extension. .stem The final component without the extension.
Examples are given in the first sample usage above.
Methods
Path objects have the following methods:
.ancestor(N)
Same as specifying .parent N times.
.child(*components) Join paths in a safe manner. The child components may not contain a path separator or be curdir or pardir ("." or ".." on Posix). This is to prevent untrusted arguments from creating a path above the original path's directory.
.components()
Return a list of directory components as strings. The first component will
be the root ("/" on Posix, a Windows drive root, or a UNC share) if the
path is absolute, or "" if it's relative. Calling Path(components),
Path(*components), or os.path.join(*components) will recreate the
original path.
.expand()
Same as p.expand_user().expand_vars().norm(). Usually this is all
you need to fix up a path read from a config file.
.expand_user() Interpolate "~" and "~user" if the platform allows, and return a new path.
.expand_vars() Interpolate environment variables like "$BACKUPS" if the platform allows, and return a new path.
.isabsolute() Is the path absolute?
.norm()
See Normalization above. Same as os.path.normpath.
.norm_case() On case-insensitive platforms (Windows) convert the path to lower case. On case-sensitive platforms (Unix) leave the path as is. This also turns forward slashes to backslashes on Windows.
.split_root()
Split this path at the root and return a tuple of two paths: the root and
the rest of the path. The root is the same as the first subscript of the
.components() result. Calling Path(root, rest) or
os.path.join(root, rest) will produce the original path.
Examples::
Path("foo/bar.py").components() =>
[Path(""), Path("foo"), Path("bar.py")]
Path("foo/bar.py").split_root() =>
(Path(""), Path("foo/bar.py"))
Path("/foo/bar.py").components() =>
[Path("/"), Path("foo"), Path("bar.py")]
Path("/foo/bar.py").split_root() =>
(Path("/"), Path("foo/bar.py"))
Path("C:\\foo\\bar.py").components() =>
["Path("C:\\"), Path("foo"), Path("bar.py")]
Path("C:\\foo\\bar.py").split_root() =>
("Path("C:\\"), Path("foo\\bar.py"))
Path("\\\\UNC_SHARE\\foo\\bar.py").components() =>
[Path("\\\\UNC_SHARE"), Path("foo"), Path("bar.py")]
Path("\\\\UNC_SHARE\\foo\\bar.py").split_root() =>
(Path("\\\\UNC_SHARE"), Path("foo\\bar.py"))
Path("~/bin").expand_user() => Path("/home/guido/bin")
Path("~timbot/bin").expand_user() => Path("/home/timbot/bin")
Path("$HOME/bin").expand_vars() => Path("/home/guido/bin")
Path("~//$BACKUPS").expand() => Path("/home/guido/Backups")
Path("dir").child("subdir", "file") => Path("dir/subdir/file")
Path("/foo").isabsolute() => True
Path("foo"
Related Skills
node-connect
346.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
