Skip to content

feat(sftp): migrate from paramiko to asyncssh (PPK v3, non-blocking IO) - #44

Merged
Orinks merged 5 commits into
devfrom
feat/ppk-key-support
Mar 2, 2026
Merged

feat(sftp): migrate from paramiko to asyncssh (PPK v3, non-blocking IO)#44
Orinks merged 5 commits into
devfrom
feat/ppk-key-support

Conversation

@Orinks

@Orinks Orinks commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace paramiko with asyncssh for all SFTP connections — asyncssh is non-blocking and handles PPK v2+v3 natively
  • Remove ppk_utils.py and puttykeys dependency — no conversion layer needed, asyncssh reads PPK files directly
  • Dedicated event loop on daemon thread eliminates UI freeze workarounds (_sftp_lock, _listdir_attr_safe, daemon threads) that paramiko required
  • Simplify host_key_policy.py and ssh_utils.py — remove paramiko-specific code
  • Update all SFTP tests to use asyncssh mocks (121 tests pass)

What changed

File Change
pyproject.toml Remove puttykeys, add asyncssh>=2.14
protocols.py Rewrite SFTPClient with asyncssh + background event loop
ppk_utils.py Deleted (asyncssh handles PPK natively)
host_key_policy.py Simplified for asyncssh known_hosts param
ssh_utils.py Remove paramiko-specific agent detection code
portkeydrop.spec Update hidden imports for asyncssh
test_*.py (6 files) Replace paramiko mocks with asyncssh mocks

Architecture

wx UI thread ──call──▶ SFTPClient.list_dir() ──_run()──▶ asyncio event loop (daemon thread)
                              │                                    │
                              │ blocks via Future.result()         │ await sftp.readdir()
                              ◀────────────────────────────────────┘

All asyncssh operations run on a persistent background event loop. The _run() helper submits coroutines and blocks until completion, keeping the wx UI thread responsive.

Preserved behavior

  • Connect/disconnect, list_directory, upload, download, delete, rename, mkdir, rmdir, stat
  • Key file auth (OpenSSH + PPK v2/v3), password auth, SSH agent auth
  • Host key policies (AUTO_ADD, STRICT, PROMPT)
  • FTP/FTPS clients untouched

Test plan

  • 121 tests pass (all SFTP-related test files)
  • Ruff check + format clean
  • No remaining paramiko/puttykeys references in source
  • Manual test: connect to SFTP server with OpenSSH key
  • Manual test: connect to SFTP server with PPK v3 key
  • Manual test: verify UI remains responsive during directory listing

🤖 Generated with Claude Code

Orinks and others added 5 commits March 2, 2026 06:49
Adds native PPK v2 support for SFTP site authentication, matching WinSCP
behaviour. Users can now select a .ppk file directly when configuring a
site — no manual conversion required.

Implementation:
- New ppk_utils.py: is_ppk_file() detects .ppk extension; load_ppk_key()
  converts PPK → OpenSSH in-memory via puttykeys + paramiko, nothing
  written to disk
- protocols.py: when key_path ends in .ppk, route through load_ppk_key()
  and pass pkey= to paramiko instead of key_filename=
- Passphrase-protected PPK files use the existing password field
- puttykeys added as a dependency (uses cryptography, already installed)

Supports RSA and Ed25519 key types (PPK v2). PPK v3 users can convert
with PuTTYgen.

Closes #43
Replace paramiko with asyncssh for all SFTP operations. asyncssh handles
PPK v2/v3 keys natively (no puttykeys conversion needed) and provides
non-blocking IO via a dedicated event loop on a background thread,
eliminating the UI freeze workarounds (daemon threads, _sftp_lock,
_listdir_attr_safe) that were needed with paramiko.

Changes:
- Rewrite SFTPClient in protocols.py to use asyncssh with a dedicated
  asyncio event loop on a daemon thread
- Remove ppk_utils.py (asyncssh handles PPK natively)
- Remove puttykeys dependency, add asyncssh>=2.14
- Simplify host_key_policy.py and ssh_utils.py (remove paramiko code)
- Update all tests to use asyncssh mocks
- Update PyInstaller spec for asyncssh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Orinks Orinks changed the title feat(sftp): support PuTTY PPK key files for SFTP authentication feat(sftp): migrate from paramiko to asyncssh (PPK v3, non-blocking IO) Mar 2, 2026
@Orinks
Orinks merged commit 75b7cae into dev Mar 2, 2026
6 checks passed
@Orinks
Orinks deleted the feat/ppk-key-support branch March 2, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant