nobodd.path
Defines the FatPath
class, a Path-like class for interacting with
directories and sub-directories in a FatFileSystem
instance. You should never need to construct this class directly; instead it
should be derived from the root
attribute
which is itself a FatPath
instance.
>>> from nobodd.disk import DiskImage
>>> from nobodd.fs import FatFileSystem
>>> img = DiskImage('test.img')
>>> fs = FatFileSystem(img.partitions[1].data)
>>> for p in fs.root.iterdir():
... print(repr(p))
...
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/foo')
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/bar.txt')
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/setup.cfg')
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/baz')
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/adir')
FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/BDIR')
FatPath
- class nobodd.path.FatPath(fs, *pathsegments)[source]
A
Path
-like object representing a filepath within an associatedFatFileSystem
.There is rarely a need to construct this class directly. Instead, instances should be obtained via the
root
property of aFatFileSystem
. If constructed directly, fs is aFatFileSystem
instance, and pathsegments is the sequence of strings to be joined with a path separator into the path.Instances provide almost all the facilities of the
pathlib.Path
class they are modeled after, including the crucialopen()
method,iterdir()
,glob()
, andrglob()
for enumerating directories,stat()
,is_dir()
, andis_file()
for querying information about files, division for construction of new paths, and all the usualname
,parent
,stem
, andsuffix
attributes. When theFatFileSystem
is writable, thenunlink()
,touch()
,mkdir()
,rmdir()
, andrename()
may also be used.Instances are also comparable for the purposes of sorting, but only within the same
FatFileSystem
instance (comparisons across file-system instances raiseTypeError
).- exists()[source]
Whether the path points to an existing file or directory:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> (fs.root / 'foo').exists() True >>> (fs.root / 'fooo').exists() False
- glob(pattern)[source]
Glob the given relative pattern in the directory represented by this path, yielding matching files (of any kind):
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> sorted((fs.root / 'nobodd').glob('*.py')) [FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/__init__.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/disk.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/fat.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/fs.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/gpt.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/main.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/mbr.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/tftp.py'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/tools.py')]
Patterns are the same as for
fnmatch()
, with the addition of “**
” which means “this directory and all subdirectories, recursively”. In other words, it enables recurisve globbing.Warning
Using the “
**
” pattern in large directory trees may consume an inordinate amount of time.
- is_absolute()[source]
Return whether the path is absolute or not. A path is considered absolute if it has a “/” prefix.
- is_dir()[source]
Return a
bool
indicating whether the path points to a directory.False
is also returned if the path doesn’t exist.
- is_file()[source]
Returns a
bool
indicating whether the path points to a regular file.False
is also returned if the path doesn’t exist.
- is_mount()[source]
Returns a
bool
indicating whether the path is a mount point. In this implementation, this is onlyTrue
for the root path.
- iterdir()[source]
When the path points to a directory, yield path objects of the directory contents:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> for child in fs.root.iterdir(): child ... FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/foo') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/bar.txt') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/setup.cfg') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/baz') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/adir') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/BDIR')
The children are yielded in arbitrary order (the order they are found in the file-system), and the special entries
'.'
and'..'
are not included.
- joinpath(*other)[source]
Calling this method is equivalent to combining the path with each of the other arguments in turn:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> fs.root FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/') >>> fs.root.joinpath('nobodd') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd') >>> fs.root.joinpath('nobodd', 'main.py') FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/main.py')
- match(pattern)[source]
Match this path against the provided glob-style pattern. Returns a
bool
indicating if the match is successful.If pattern is relative, the path may be either relative or absolute, and matching is done from the right:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> f = fs / 'nobodd' / 'mbr.py' >>> f FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd/mbr.py') >>> f.match('*.py') True >>> f.match('nobodd/*.py') True >>> f.match('/*.py') False
As FAT file-systems are case-insensitive, all matches are likewise case-insensitive.
- mkdir(mode=511, parents=False, exist_ok=False)[source]
Create a new directory at this given path. The mode parameter exists only for compatibility with
pathlib.Path
and is otherwise ignored. If the path already exists,FileExistsError
is raised.If parents is true, any missing parents of this path are created as needed.
If parents is false (the default), a missing parent raises
FileNotFoundError
.If exist_ok is false (the default),
FileExistsError
is raised if the target directory already exists.If exist_ok is true,
FileExistsError
exceptions will be ignored (same behavior as the POSIXmkdir -p
command), but only if the last path component is not an existing non-directory file.
- open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)[source]
Open the file pointed to by the path, like the built-in
open()
function does. The mode, buffering, encoding, errors and newline options are as for theopen()
function. If successful, aFatFile
instance is returned.Note
This implementation is read-only, so any modes other than “r” and “rb” will fail with
PermissionError
.
- read_bytes()[source]
Return the binary contents of the pointed-to file as a bytes object:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> (fs.root / 'foo').read_text() b'foo\n'
- read_text(encoding=None, errors=None)[source]
Return the decoded contents of the pointed-to file as a string:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> (fs.root / 'foo').read_text() 'foo\n'
- relative_to(*other)[source]
Compute a version of this path relative to the path represented by other. If it’s impossible,
ValueError
is raised.
- rename(target)[source]
Rename this file or directory to the given target, and return a new
FatPath
instance pointing to target. If target exists and is a file, it will be replaced silently. target can be either a string or another path object:>>> p = fs.root / 'foo' >>> p.open('w').write('some text') 9 >>> target = fs.root / 'bar' >>> p.rename(target) FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/bar') >>> target.read_text() 'some text'
The target path must be absolute. There are no guarantees of atomic behaviour (in contrast to
os.rename()
).Note
pathlib.Path.rename()
permits relative paths, but interprets them relative to the working directory which is a conceptFatPath
does not support.
- resolve(strict=False)[source]
Make the path absolute, resolving any symlinks. A new
FatPath
object is returned.".."
components are also eliminated (this is the only method to do so). If the path doesn’t exist and strict isTrue
,FileNotFoundError
is raised. If strict isFalse
, the path is resolved as far as possible and any remainder is appended without checking whether it exists.Note that as there is no concept of the “current” directory within
FatFileSystem
, relative paths cannot be resolved by this function, only absolute.
- rglob(pattern)[source]
This is like calling
glob()
with a prefix of “**/
” to the specified pattern.
- stat(*, follow_symlinks=True)[source]
Return a
os.stat_result
object containing information about this path:>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.stat().st_size 388 >>> p.stat().st_ctime 1696606672.02
Note
In a FAT file-system,
atime
has day resolution,mtime
has 2-second resolution, andctime
has either 2-second or millisecond resolution depending on the driver that created it. Directories have no timestamp information.The follow_symlinks parameter is included purely for compatibility with
pathlib.Path.stat()
; it is ignored as symlinks are not supported.
- touch(mode=438, exist_ok=True)[source]
Create a file at this given path. The mode parameter is only present for compatibility with
pathlib.Path
and is otherwise ignored. If the file already exists, the function succeeds if exist_ok isTrue
(and its modification time is updated to the current time), otherwiseFileExistsError
is raised.
- unlink(missing_ok=False)[source]
Remove this file. If the path points to a directory, use
rmdir()
instead.If missing_ok is
False
(the default),FileNotFoundError
is raised if the path does not exist. If missing_ok isTrue
,FileNotFoundError
exceptions will be ignored (same behaviour as the POSIXrm -f
command).
- with_name(name)[source]
Return a new path with the
name
changed. If the original path doesn’t have a name,ValueError
is raised.
- with_stem(stem)[source]
Return a new path with the
stem
changed. If the original path doesn’t have a name,ValueError
is raised.
- with_suffix(suffix)[source]
Return a new path with the
suffix
changed. If the original path doesn’t have a suffix, the new suffix is appended instead. If the suffix is an empty string, the original suffix is removed.
- write_bytes(data)[source]
Open the file pointed to in bytes mode, write data to it, and close the file:
>>> p = fs.root / 'my_binary_file' >>> p.write_bytes(b'Binary file contents') 20 >>> p.read_bytes() b'Binary file contents'
An existing file of the same name is overwritten.
- write_text(data, encoding=None, errors=None, newline=None)[source]
Open the file pointed to in text mode, write data to it, and close the file:
>>> p = fs.root / 'my_text_file' >>> p.write_text('Text file contents') 18 >>> p.read_text() 'Text file contents'
An existing file of the same name is overwritten. The optional parameters have the same meaning as in
open()
.
- property anchor
Returns the concatenation of the drive and root. This is always “/”.
- property fs
Returns the
FatFileSystem
instance that this instance was constructed with.
- property name
A string representing the final path component, excluding the root:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.name 'main.py'
- property parent
The logical parent of the path:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.parent FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd')
You cannot go past an anchor:
>>> p = (fs.root / 'nobodd' / 'main.py') >>> p.parent.parent.parent FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/')
- property parents
An immutable sequence providing access to the logical ancestors of the path:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.parents (FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/nobodd'), FatPath(<FatFileSystem label='TEST' fat_type='fat16'>, '/'))
- property parts
A tuple giving access to the path’s various components:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.parts ['/', 'nobodd', 'main.py']
- property root
Returns a string representing the root. This is always “/”.
- property stem
The final path component, without its suffix:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.stem 'main'
- property suffix
The file extension of the final component, if any:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd' / 'main.py') >>> p.suffix '.py'
- property suffixes
A list of the path’s file extensions:
>>> fs <FatFileSystem label='TEST' fat_type='fat16'> >>> p = (fs.root / 'nobodd.tar.gz') >>> p.suffixes ['.tar', '.gz']
Internal Functions
- nobodd.path.get_cluster(entry, fat_type)[source]
Given entry, a
DirectoryEntry
, and the fat_type indicating the size of FAT clusters, return the first cluster of the file associated with the directory entry.