Providers API
What a class built on Path calls: the error and follow policies, the name and
mode checks, the commit-on-close buffer, the LRU cache and contains.
pathapi.providers
What a class built on Path calls: error and follow policies, name and
mode checks, the split of a name into stem and suffix, the commit-on-close
buffer, the LRU cache and the contains test.
UNCHANGED = None
module-attribute
__all__ = ['CommitOnClose', 'DiscardOnError', 'LRU', 'OfferedOnce', 'TreePath', 'UNCHANGED', 'as_error_handler', 'as_error_predicate', 'check_follow', 'contains', 'copy_error_predicate', 'is_safe_child_name', 'is_windows_flavoured', 'name_stem', 'name_suffix', 'name_suffixes', 'derived_from', 'notimplemented', 'primitive', 'parse_mode', 'parse_owner']
module-attribute
CommitOnClose(commit, initial=None)
Bases: DiscardOnError, BytesIO
A write buffer held in memory whose content is handed to commit when
it is closed, once, if it was written to. For a backend with no way to
write a file piecewise (an FTP STOR, a rewrite of an archive): the cost
is the whole file in memory for the duration of the write.
initial (an open("r+")) is the file's content, with the position at 0,
and commit runs only if the buffer was modified; without it, commit
runs at the first close even if nothing was written, which creates an
empty file. commit(buffer) runs before the buffer is closed, so it can
read from it.
discard() drops the buffer so that a later close() commits nothing;
leaving a with block through an exception calls it before closing, and
so does collecting the stream unclosed, which also warns.
Source code in src/pathapi/_commit.py
74 75 76 77 78 79 80 81 | |
close()
Source code in src/pathapi/_commit.py
107 108 109 110 111 112 113 114 115 116 | |
discard()
Drop what was written: close() commits nothing, whatever is
written after this. Calling it twice, or after close(), does
nothing more.
Source code in src/pathapi/_commit.py
98 99 100 101 102 103 104 105 | |
truncate(size=None)
Source code in src/pathapi/_commit.py
91 92 93 | |
write(data)
Source code in src/pathapi/_commit.py
83 84 85 | |
writelines(lines)
Source code in src/pathapi/_commit.py
87 88 89 | |
DiscardOnError
Mixin for a write stream that commits when it is closed. Leaving a
with block through an exception (any BaseException) calls discard()
and then closes; leaving it normally just closes. A stream that is
garbage collected unclosed while _holds_uncommitted() is true is
discarded too, with a ResourceWarning: what was never finished is not
committed. Put it first in the bases, so that its __exit__ and
__del__ win over io.IOBase's.
__slots__ = ()
class-attribute
instance-attribute
__del__()
Source code in src/pathapi/_commit.py
32 33 34 35 36 37 38 39 40 41 42 43 | |
__exit__(exc_type, exc_value, traceback)
Source code in src/pathapi/_commit.py
45 46 47 48 49 50 51 52 | |
discard()
Drop what was written so that close() commits nothing. It does
nothing more when called twice or after close().
Source code in src/pathapi/_commit.py
21 22 23 24 | |
LRU(func, maxsize=128, on_evict=None)
Bases: Generic[K, V]
Thread-safe memoizing LRU cache over a function, callable like the
function itself; invalidate(*args) evicts and recomputes an entry,
discard(*args) evicts without recomputing.
on_evict(key, value), when given, is called for every value the cache
drops -- LRU overflow, a maxsize shrink, invalidate()/discard(),
and the losing result of two concurrent misses on one key -- so a cache
of live resources (connections) can close them instead of leaking them.
It runs outside the lock, and an exception it raises is suppressed:
eviction is a side effect of an unrelated lookup, which must not fail
because a stale resource would not close cleanly.
The computation itself runs outside the lock, so concurrent misses on
one key may each call func; the first stored result wins and is
returned to every caller, and the others are passed to on_evict.
maxsize follows functools.lru_cache: None is unbounded, and 0 (or
less) stores nothing, so func runs on every call and its result is
returned without reaching on_evict.
Source code in src/pathapi/_lru.py
64 65 66 67 68 69 70 71 72 73 74 | |
cache = collections.OrderedDict()
instance-attribute
func = func
instance-attribute
lock = RLock()
instance-attribute
maxsize
property
writable
on_evict = on_evict
instance-attribute
__call__(*args)
Source code in src/pathapi/_lru.py
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
discard(*args)
Drop the entry for args (passing it to on_evict) without
recomputing it. Returns whether an entry was present.
Source code in src/pathapi/_lru.py
126 127 128 129 130 131 132 133 134 | |
invalidate(*args)
Source code in src/pathapi/_lru.py
136 137 138 | |
OfferedOnce(predicate)
An (error, path) -> bool error predicate that sees each error once.
True swallows the error. An error it answered False for, and an
exception it raised itself, are not offered again when they travel up
through the enclosing directories of a recursive operation: they are
raised as they are. Build one with as_error_predicate().
Source code in src/pathapi/_errorpolicy.py
60 61 62 | |
__slots__ = ('_predicate', '_seen')
class-attribute
instance-attribute
__call__(error, path)
Source code in src/pathapi/_errorpolicy.py
64 65 66 67 68 69 70 71 72 73 74 75 | |
TreePath
Bases: Path
A Path whose names are POSIX segments below the root of a tree that a
backend serves (a remote directory, the members of an archive).
Joined and normalized like PurePosixPath: empty and "." segments
collapse and an absolute argument restarts the join. Relative and rooted
spellings name the same node, because the tree has no working directory.
A subclass supplies the constructor, _rebuild() and the backend it
carries; this class holds the names only.
__slots__ = ('_segments',)
class-attribute
instance-attribute
parent
property
parts
property
segments
property
__copy__()
Source code in src/pathapi/_tree.py
76 77 | |
__deepcopy__(memo)
Source code in src/pathapi/_tree.py
79 80 81 82 | |
__str__()
Source code in src/pathapi/_tree.py
73 74 | |
relative_to(other, *, walk_up=False)
self below other, as a relative path on the same backend.
other is a path of this class on the same tree or a str. Names are
compared as written, with no .. applied. ValueError when self
is not below other, when the two differ in being rooted, or when
the trees differ. With walk_up, a path beside other gets a ..
per level climbed; a .. in other cannot be walked.
Source code in src/pathapi/_tree.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
with_segments(*segments)
Source code in src/pathapi/_tree.py
104 105 | |
as_error_handler(ignore_error, *, default=False)
Normalize an ignore_error argument into a callable error policy.
Every ignore_error (and copy()'s on_error) parameter in this library
accepts either a bool or a callable, but the callables have different
arities per call site (Path.rm() and Path.copy(on_error=) ->
(error, path), Path.copy(ignore_error=) -> (error),
PathSyncer.sync() -> (error, source, target, event)). Changing the
arity of an existing keyword would break its callers, so this helper only
normalizes the bool case and passes a supplied callable through
untouched -- it is invoked with whatever arguments its own call site
already uses.
None means "no policy supplied": it resolves to default (False for
every current caller, i.e. raise on the first error), which preserves
Path.copy(ignore_error=None)'s documented meaning.
Centralizing this keeps a fourth call site from drifting back into
calling a bool (see PathSyncer.sync()'s symlink branch, which did
exactly that and raised TypeError: 'bool' object is not callable).
Source code in src/pathapi/_errorpolicy.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
as_error_predicate(on_error)
on_error as an OfferedOnce predicate: a bool becomes a constant
answer (None is False), a callable is called as (error, path), and
an OfferedOnce is returned as it is, so a recursive call that is handed
the predicate of its caller shares what that predicate has seen.
Source code in src/pathapi/_errorpolicy.py
78 79 80 81 82 83 84 85 86 87 | |
check_follow(name, policy)
Raise ValueError naming name unless policy is True, False,
None or a callable: what rm() accepts for follow_symlinks= and
follow_binds=. An rm() override calls it before removing anything.
Source code in src/pathapi/_errorpolicy.py
90 91 92 93 94 95 | |
contains(src, target)
Whether target is src or lies inside it (pathlib 3.14's copy()
refuses both), looking through symlinks. Paths _same_filesystem()
reports in different namespaces are never nested.
Source code in src/pathapi/protocols/_relation.py
152 153 154 155 156 157 158 159 160 | |
copy_error_predicate(ignore_error, on_error)
The (error, path) -> bool predicate for copy()'s on_error=, or
None when it was not given. Raises TypeError when ignore_error= was
given too: a copy() override calls this before it copies anything.
Source code in src/pathapi/_errorpolicy.py
98 99 100 101 102 103 104 105 106 | |
derived_from(*primitives)
Mark a method as built on other primitives of the class, such as an
iterdir() that lists through _listdir(): protocols.supports() counts
it as implemented only when one of those primitives is.
Source code in src/pathapi/_stubs.py
49 50 51 52 53 54 55 56 57 58 | |
is_safe_child_name(name, *, windows=False)
Whether name is a single path component that stays inside its parent.
Use this before joining a name that came from somewhere untrusted (a
remote listing, an archive member, a sync source) onto a destination.
Always rejected: a non-str, "", ".", "..", and any name
containing / or NUL. With windows=True (see is_windows_flavoured)
also rejected: \, : (a drive prefix such as D:x, or an NTFS
alternate data stream), a trailing dot or space (Windows drops it, so
"report." and "report" name one file), and the reserved device names
CON, PRN, AUX, NUL, COM1-COM9 and LPT1-LPT9, in any case
and with or without an extension ("nul.txt").
Source code in src/pathapi/_names.py
57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
is_windows_flavoured(path)
Whether names joined onto path are interpreted with Windows rules.
True for a pathlib.PureWindowsPath (so LocalPath on Windows) and for
a path whose filepath is one (a file: URI path on Windows). Everything else --
MemPath, remote schemes, POSIX local paths -- treats \ and : as
ordinary filename characters.
Source code in src/pathapi/_names.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
name_stem(name)
Source code in src/pathapi/path/_pure.py
67 68 69 | |
name_suffix(name)
Source code in src/pathapi/path/_pure.py
63 64 65 | |
name_suffixes(name)
Source code in src/pathapi/path/_pure.py
71 72 73 74 | |
notimplemented(method)
Mark a method as the stub of a primitive. Used bare (@notimplemented)
or with the reason a backend gives (@notimplemented("why")); calling the
stub raises NotImplementedError with the reason, or with the method's
name.
Source code in src/pathapi/_stubs.py
23 24 25 26 27 28 29 30 31 | |
parse_mode(mode)
Normalize a permission mode to an int, parsing str as octal.
chmod("0755") is the spelling everyone actually writes a mode in --
chmod(1), Ansible, Dockerfiles, every shell script -- and stdlib
refuses it (TypeError: 'str' object cannot be interpreted as an
integer). This library accepts it, which makes the base explicit and
non-negotiable rather than leaving it to each call site.
Why base 8 is mandatory here, and never a plain int(): "0755"
parsed as decimal is 755, which is 0o1363 -- a different and valid
mode. Nothing would raise; the file would just end up with permissions
nobody intended. That is exactly why stdlib declines strings, so the
only safe way to accept them is to parse them one way, in one place.
Accepts an optional 0o/0O prefix. Anything outside [0-7] raises
ValueError rather than being coerced -- a mode is not a number that
happens to be written in octal, it is octal.
An int passes through (including 0o755, which is an int by the time
it gets here -- the literal is resolved by the parser, so chmod(0o755)
and chmod("0755") agree). A bool is TypeError, and a number that
cannot be a mode (negative, or wider than the 16 bits of a st_mode) is
ValueError, for an int and a str alike. The file-type bits of a
st_mode are in range, as os.chmod accepts them; stat.S_IMODE() drops
them.
Source code in src/pathapi/_permissions.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
parse_owner(uid, gid)
Normalize a chown() uid/gid pair to canonical int | None.
None means "leave unchanged". -1 is accepted as an alias for it,
since that is how os.chown spells the same thing and callers arriving
from the stdlib reach for it out of habit.
The point of centralizing this is that every backend spells
"unchanged" differently -- os.chown wants -1, SFTP setstat wants
the field omitted from the attrs entirely, and other middlewares want
None. If each scheme translated the caller's input itself, that is
three chances for the semantics to disagree. Normalizing once on Path
means a backend's _chown() receives an already-canonical pair and only
has to convert to its own wire spelling.
A str is passed through as a name (shutil.chown accepts user and
group names, and it is useful not to force a caller to resolve them) --
backends that cannot resolve names should say so rather than guess.
Source code in src/pathapi/_permissions.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | |
primitive(method)
Mark method as the default of a primitive: it raises
NotImplementedError, and a class that does not replace it does not
implement the primitive.
Source code in src/pathapi/_stubs.py
15 16 17 18 19 20 | |