Nora
f62e1a7881
fix(sp37-followup): add SftpClient.stat() method
...
The SftpClient wrapper previously exposed write_file, list_inbound,
list_inbound_names, download_inbound, and read_file — but NO stat().
The canonical cyclone.submission.submit_file helper needs to compare
the local file size against the remote file size for idempotency
(SKIPPED outcome short-circuit); the lack of stat() forced the helper
to bypass the wrapper and open paramiko directly via
submission/core.py:_default_sftp_factory.
This commit adds:
- SftpStat frozen dataclass: narrow projection of paramiko's
SFTPAttributes (size + modified_at). Callers don't need paramiko.
- SftpClient.stat(remote_path) -> SftpStat public method
- _stat_stub + _stat_paramiko backends (mirrors the existing
_read_file_stub / _read_file_paramiko pattern)
- 8 new tests covering dataclass shape, frozen invariant, stub size,
stub mtime, missing-file FileNotFoundError, large-file size, and
the actual SKIPPED-outcome idempotency check from submit_file
Also adds backend/var/ to .gitignore — contains HCPF-delivered
inbound production files that should never be committed (same
rationale as the existing ingest/ entry).
2026-07-07 12:24:35 -06:00
..
2026-07-07 10:50:48 -06:00
2026-07-02 17:08:09 -06:00
2026-06-20 18:29:28 -06:00
2026-06-24 17:51:44 -06:00
2026-06-29 10:09:10 -06:00
2026-07-07 10:34:46 -06:00
2026-07-06 09:53:53 -06:00
2026-07-06 09:52:51 -06:00
2026-07-06 09:53:53 -06:00
2026-07-02 09:00:54 -06:00
2026-06-20 23:45:43 -06:00
2026-06-21 09:43:51 -06:00
2026-06-22 11:01:58 -06:00
2026-07-02 12:22:29 -06:00
2026-06-20 19:37:38 -06:00
2026-06-24 22:05:55 -06:00
2026-06-29 11:00:37 -06:00
2026-06-20 08:22:31 -06:00
2026-07-02 14:18:55 -06:00
2026-06-20 19:35:17 -06:00
2026-06-22 11:01:58 -06:00
2026-06-20 08:03:37 -06:00
2026-06-20 18:58:56 -06:00
2026-06-29 14:32:10 -06:00
2026-07-06 14:01:37 -06:00
2026-06-24 22:05:55 -06:00
2026-06-20 20:34:44 -06:00
2026-06-20 16:57:30 -06:00
2026-07-07 12:19:42 -06:00
2026-07-06 09:53:53 -06:00
2026-06-21 10:46:10 -06:00
2026-07-06 09:51:27 -06:00
2026-06-20 23:41:01 -06:00
2026-07-02 21:07:07 -06:00
2026-07-02 12:22:29 -06:00
2026-06-22 15:34:18 -06:00
2026-06-20 23:45:43 -06:00
2026-06-22 15:59:08 -06:00
2026-06-23 17:24:23 -06:00
2026-06-22 15:34:05 -06:00
2026-06-22 15:59:08 -06:00
2026-07-07 12:17:14 -06:00
2026-06-22 15:59:08 -06:00
2026-06-22 15:59:08 -06:00
2026-06-22 15:59:08 -06:00
2026-07-02 17:16:59 -06:00
2026-06-21 09:43:51 -06:00
2026-06-21 09:43:51 -06:00
2026-06-21 09:43:51 -06:00
2026-06-20 17:17:09 -06:00
2026-07-07 10:30:23 -06:00
2026-07-07 10:22:32 -06:00
2026-06-20 07:44:41 -06:00
2026-06-24 22:05:55 -06:00
2026-06-19 17:07:54 -06:00
2026-06-21 09:43:51 -06:00
2026-06-22 15:34:18 -06:00
2026-06-21 10:46:10 -06:00
2026-06-19 16:00:47 -06:00
2026-06-19 17:01:57 -06:00
2026-07-02 21:50:23 -06:00
2026-07-02 20:23:56 -06:00
2026-06-29 12:51:28 -06:00
2026-06-21 00:33:37 -06:00
2026-07-07 10:13:00 -06:00
2026-07-02 14:59:49 -06:00
2026-06-19 21:03:21 -06:00
2026-06-24 16:09:19 -06:00
2026-07-02 12:22:29 -06:00
2026-06-19 15:31:06 -06:00
2026-06-22 15:59:08 -06:00
2026-06-24 23:23:46 -06:00
2026-06-29 10:46:29 -06:00
2026-06-29 12:08:47 -06:00
2026-07-02 08:52:20 -06:00
2026-06-29 10:38:32 -06:00
2026-06-29 11:30:00 -06:00
2026-06-20 19:43:43 -06:00
2026-06-20 23:41:01 -06:00
2026-07-02 13:16:38 -06:00
2026-06-20 18:25:25 -06:00
2026-06-22 17:46:47 -06:00
2026-06-21 00:13:47 -06:00
2026-06-20 19:29:21 -06:00
2026-06-29 13:22:42 -06:00
2026-06-21 09:59:34 -06:00
2026-06-21 09:59:34 -06:00
2026-06-21 09:59:34 -06:00
2026-07-07 12:21:27 -06:00
2026-07-07 12:21:27 -06:00
2026-06-20 08:22:31 -06:00
2026-06-20 08:22:31 -06:00
2026-06-19 17:01:57 -06:00
2026-06-20 08:03:37 -06:00
2026-06-19 15:39:08 -06:00
2026-06-21 10:46:10 -06:00
2026-06-20 08:22:31 -06:00
2026-06-20 08:22:31 -06:00
2026-06-20 23:41:01 -06:00
2026-07-02 16:56:01 -06:00
2026-07-02 21:50:23 -06:00
2026-06-25 00:26:13 -06:00
2026-06-20 18:58:56 -06:00
2026-06-20 23:41:01 -06:00
2026-06-20 23:07:31 -06:00
2026-06-21 00:13:47 -06:00
2026-07-02 21:50:23 -06:00
2026-07-02 21:07:07 -06:00
2026-06-20 20:27:48 -06:00
2026-07-02 16:23:42 -06:00
2026-06-24 22:05:55 -06:00
2026-06-20 16:57:30 -06:00
2026-06-20 19:33:02 -06:00
2026-07-02 17:08:09 -06:00
2026-06-24 15:36:19 -06:00
2026-06-29 11:53:56 -06:00
2026-06-24 23:23:46 -06:00
2026-06-20 18:30:05 -06:00
2026-06-24 15:29:52 -06:00
2026-06-24 16:06:54 -06:00
2026-06-20 23:07:31 -06:00
2026-06-21 10:21:01 -06:00
2026-06-19 15:36:01 -06:00
2026-06-20 08:22:31 -06:00
2026-06-22 11:01:58 -06:00
2026-06-20 08:03:37 -06:00
2026-06-20 19:27:24 -06:00
2026-07-07 12:24:35 -06:00
2026-06-29 11:43:58 -06:00
2026-06-24 23:23:46 -06:00
2026-06-24 22:05:55 -06:00
2026-07-02 08:47:03 -06:00
2026-06-29 14:32:10 -06:00
2026-06-29 12:21:34 -06:00
2026-06-29 14:32:10 -06:00
2026-07-02 08:45:19 -06:00
2026-06-24 22:05:55 -06:00
2026-06-20 18:10:07 -06:00
2026-07-07 12:17:50 -06:00
2026-07-07 12:19:42 -06:00
2026-06-20 23:07:31 -06:00
2026-06-19 17:01:57 -06:00
2026-06-21 10:46:10 -06:00
2026-06-19 15:56:09 -06:00