sftp:
Needs the sftp extra (paramiko backend) or the sftp-async extra (asyncssh
backend).
pathlib_next.uri.schemes.sftp
BaseSftpBackend
Bases: object
Protocol for obtaining a paramiko-shaped SFTPClient for a
Source. Subclass this to plug in custom connection handling (e.g.
tests mock it directly, no real server); SftpBackend (paramiko,
_paramiko.py) and AsyncsshSftpBackend (_asyncssh.py, optional
extra) are the real implementations. Connection caching is each
backend's own responsibility -- client() is expected to return an
already-cached-or-freshly-opened, ready-to-use client; SftpPath
itself does no per-backend branching anywhere.
checksum(path, algorithm)
Backend-native digest for path's content, e.g. via the
filexfer draft's check-file-handle SFTP extension (implemented by
some servers, e.g. ProFTPD's mod_sftp; OpenSSH has no such
extension). Raises
NotImplementedError (the base/default here) when the backend has
no such capability at all, and MUST also raise it -- not return a
value -- when the server doesn't advertise algorithm specifically
(see protocols/checksum.py::NativeChecksum.checksum for why this
is a hard contract, not a style choice). Both real backends
implement it through _checkfile.py::CheckFileSftpBackend, each
supplying only the extended request itself.
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | |
close()
Close every connection this backend has cached. A no-op here;
both real backends override it. The backend stays usable: the next
client() call reconnects.
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
78 79 80 81 | |
supported_checksums(path)
Advisory set of algorithm names checksum() can currently
produce against path's server connection (see
protocols.checksum.NativeChecksum.supported_checksums). Empty
here (the default): no native-hashing support at all. Both real
backends override this with a per-connection probe against path
(neither library exposes the server's extension list -- see
_checkfile.py::CheckFileSftpBackend.supported_checksums). Takes a
path argument (unlike a bare capability flag) because the only
reliable way to know is to actually try the extension against a
real file.
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
61 62 63 64 65 66 67 68 69 70 71 72 73 | |
SftpPath(*uris, **options)
Bases: UriPath
sftp: scheme: full read/write access, auto-selecting between a
paramiko (sync) and an asyncssh (async, bridged) backend -- see
"backend selection" above. Requires the sftp extra (paramiko) or
sftp-async extra (asyncssh). Also implements
protocols.checksum.NativeChecksum (checksum(), delegating to
self.backend.checksum()) -- native on both backends via the filexfer
draft's check-file-handle extension where the server implements it
(OpenSSH does not), NotImplementedError (falls back to streaming) on a
server without that extension.
Source code in src/pathlib_next/uri/__init__.py
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
checksum(algorithm='md5')
protocols.checksum.NativeChecksum implementation: delegates to
self.backend.checksum() (the check-file-handle SFTP extension
on both backends -- see BaseSftpBackend.checksum). Any failure
that isn't already
NotImplementedError (a server that doesn't advertise the
extension, an unsupported algorithm, a transport-level error) is
also translated to NotImplementedError: this method's whole
contract is "raise if a genuine digest can't be produced", and a
caller (e.g. PathSyncer) must be able to fall back to streaming
on ANY such failure, not just the backend's own explicit signal.
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
copy(target, *, overwrite=False, follow_symlinks=True, preserve_metadata=True, recursive=False, ignore_error=None, progress=None)
Copy with concurrent fan-out on the asyncssh backend.
When using the asyncssh backend with recursive=True on a
directory, child copies are fanned out as concurrent native requests,
bounded by backend.max_concurrency (requests in flight, and files
open at once); the whole copy has no wall-clock timeout. progress
is honored on the
generic single-file fallback path below, but not called during
the concurrent native fan-out itself -- see docs/divergences.md's
"Deliberate extensions" section for the documented limitation.
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | |
supported_checksums()
protocols.checksum.NativeChecksum implementation: delegates to
self.backend.supported_checksums(self) -- a real per-connection
probe on both backends (see
_checkfile.py::CheckFileSftpBackend.supported_checksums), so this
is empty when the connected server doesn't implement
check-file-handle (OpenSSH never does).
Source code in src/pathlib_next/uri/schemes/sftp/__init__.py
442 443 444 445 446 447 448 449 450 | |
pathlib_next.uri.schemes.sftp._paramiko.SftpBackend(connect_opts=None, hostkeypolicy=None, ssh_config=_DEFAULT_SSH_CONFIG, *, known_hosts=_DEFAULT_KNOWN_HOSTS, timeout=DEFAULT_TIMEOUT)
Bases: CheckFileSftpBackend
Connects via paramiko.SSHClient using connect_opts merged with
the Source's host/port/userinfo. client() caches per
(self, source, calling-thread) -- see _CACHED_CLIENTS above -- and
replaces (and closes) a cached client whose connection has dropped;
close() closes every connection the backend opened.
Host keys are verified by default. known_hosts (default: the user's
~/.ssh/known_hosts plus any ssh_config UserKnownHostsFile for the
host; None loads no file; a path or iterable of paths loads exactly
those) is loaded read-only, and hostkeypolicy (default
paramiko.RejectPolicy()) decides what happens to a key found in none
of them. A key that differs from a known one always raises
paramiko.BadHostKeyException. Opt-out, in code only:
SftpBackend(connect_opts, paramiko.AutoAddPolicy(), known_hosts=None)
accepts any server key -- a network man-in-the-middle then receives the
URI password.
timeout (default 30 s) is the default for paramiko's timeout,
banner_timeout, auth_timeout and channel_timeout connect options;
a value in connect_opts wins, and timeout=None leaves them unset.
Individual SFTP requests on an established connection are not bounded.
ssh_config support is paramiko's own (HostName, Port, User,
IdentityFile, ProxyCommand) plus Include. ProxyJump is not
supported: a host whose config sets it raises NotImplementedError
unless connect_opts supplies a sock -- use the asyncssh backend, or
an equivalent ProxyCommand.
Source code in src/pathlib_next/uri/schemes/sftp/_paramiko.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
close()
Close every cached connection this backend opened, on any thread.
Source code in src/pathlib_next/uri/schemes/sftp/_paramiko.py
260 261 262 263 264 | |
pathlib_next.uri.schemes.sftp._asyncssh.AsyncsshSftpBackend(connect_opts=None, *, max_concurrency=None, sftp_version=4, ssh_config=_DEFAULT_SSH_CONFIG, timeout=_DEFAULT_TIMEOUT)
Bases: CheckFileSftpBackend
sftp: backend using asyncssh instead of paramiko. Selected
automatically when asyncssh is importable (see backend selection in
sftp/__init__.py), or explicitly via backend=AsyncsshSftpBackend()
/PATHLIB_NEXT_SFTP_BACKEND=asyncssh. Connections are cached per
(self, source) (see _ConnectionCache above) and served through a
single shared background asyncio loop (see _run above) -- not
fork-safe (a fork()ed child inherits a dead loop thread; detected via
stored PID, loop+cache lazily recreated when os.getpid() changes).
close() closes every connection the backend opened.
Host keys are verified by default: asyncssh checks the server key
against ~/.ssh/known_hosts and the ssh_config's UserKnownHostsFile,
and an unknown or changed key fails the connection. Opt-out, in code
only: AsyncsshSftpBackend(connect_opts={"known_hosts": None}) accepts
any server key -- a network man-in-the-middle then receives the URI
password. Any other asyncssh known_hosts value (a file, a list of
keys) is passed through as given.
timeout (default _DEFAULT_TIMEOUT, 60 s) bounds a single request --
connect, stat, open, mkdir, rename, ... -- and a timed-out request is
cancelled and raises TimeoutError. Recursive copy()/rm(),
streaming file reads/writes (read_bytes(), write_bytes(), chunked
copies) and a native checksum() (the server hashes the whole file)
have no wall-clock bound; asyncssh's own connect_timeout,
login_timeout and keepalive_interval connect options detect a dead
peer there. timeout=None disables the per-request bound too.
Source code in src/pathlib_next/uri/schemes/sftp/_asyncssh.py
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 | |
close()
Close every cached connection this backend opened.
Source code in src/pathlib_next/uri/schemes/sftp/_asyncssh.py
817 818 819 | |