a11e051f82
Replace SftpClient stub write_file/list_inbound/read_file implementations with real paramiko SSHClient + SFTPClient calls. The public API (SftpClient.write_file, list_inbound, read_file, get_secret) is unchanged from SP9 — same signature, same return types — so the API layer needs no changes. Real-mode behavior: * _connect() returns a context manager yielding (ssh, sftp); closes both on exit. Lazy-imports paramiko so the stub-only test path doesn't need the dependency. * Auth resolves from SftpBlock.auth: password_keychain_account (MFT model) or key_file + optional key_passphrase_keychain_account. Missing Keychain entries fail loud (RuntimeError) rather than silently attempting empty-password auth. * write_file: opens sftp.open(remote, 'wb') and writes bytes; mkdirs the parent dir (idempotent — MFT pre-creates FromHPE/ToHPE). * list_inbound: listdir_attr + per-file download into local staging cache; skips directory entries (0o040000 mask). * read_file: download via shutil.copyfileobj into BytesIO. Stub mode is unchanged. AutoAddPolicy for first-time MFT host fingerprint; operator should pin the key for production. Adds tests/test_sftp_paramiko.py: 9 tests covering * stub still works * real-mode connect builds correct paramiko call from password_keychain_account, raises on missing Keychain, raises on missing auth config, raises on STUB_SECRET * write_file opens 'wb' on the right path and writes bytes * list_inbound translates attrs into InboundFile records and caches files locally; skips dirs Removes 2 obsolete tests in test_sftp_stub.py that expected SP13-mode to raise NotImplementedError. pyproject.toml: new optional 'sftp' extra (paramiko>=3.4,<6).
49 lines
1.1 KiB
TOML
49 lines
1.1 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=68", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "cyclone"
|
|
version = "0.1.0"
|
|
description = "Cyclone EDI suite — X12 837P/835 parser"
|
|
requires-python = ">=3.11"
|
|
dependencies = [
|
|
"pydantic>=2.6,<3",
|
|
"click>=8.1,<9",
|
|
"fastapi>=0.110,<1",
|
|
"uvicorn[standard]>=0.27,<1",
|
|
"python-multipart>=0.0.9,<1",
|
|
"sqlalchemy>=2.0,<3",
|
|
"pyyaml>=6.0,<7",
|
|
"keyring>=25.0,<26",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8.0",
|
|
"pytest-cov>=4.1",
|
|
"pytest-asyncio>=0.23,<1",
|
|
"httpx>=0.27,<1",
|
|
]
|
|
sqlcipher = [
|
|
# SP12: encryption at rest. Optional — without it the DB is plain SQLite.
|
|
# Install via: pip install -e .[sqlcipher] (after brew install sqlcipher).
|
|
"sqlcipher3>=0.6,<1",
|
|
]
|
|
sftp = [
|
|
# SP13: real SFTP wire-up. Optional — without it the stub keeps working.
|
|
# Install via: pip install -e .[sftp].
|
|
"paramiko>=3.4,<6",
|
|
]
|
|
|
|
[project.scripts]
|
|
cyclone = "cyclone.cli:main"
|
|
|
|
[tool.setuptools.packages.find]
|
|
where = ["src"]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
addopts = "-ra --strict-markers"
|
|
asyncio_mode = "auto"
|