sftp-helper 2.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sftp_helper-2.2.3/LICENSE +29 -0
- sftp_helper-2.2.3/PKG-INFO +258 -0
- sftp_helper-2.2.3/README.md +212 -0
- sftp_helper-2.2.3/pyproject.toml +105 -0
- sftp_helper-2.2.3/setup.cfg +4 -0
- sftp_helper-2.2.3/sftp_helper/__init__.py +57 -0
- sftp_helper-2.2.3/sftp_helper/api.py +386 -0
- sftp_helper-2.2.3/sftp_helper/cli_argparse.py +531 -0
- sftp_helper-2.2.3/sftp_helper/cli_click.py +324 -0
- sftp_helper-2.2.3/sftp_helper/main.py +564 -0
- sftp_helper-2.2.3/sftp_helper/mcp.py +89 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/PKG-INFO +258 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/SOURCES.txt +19 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/dependency_links.txt +1 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/entry_points.txt +4 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/requires.txt +25 -0
- sftp_helper-2.2.3/sftp_helper.egg-info/top_level.txt +1 -0
- sftp_helper-2.2.3/tests/test_api.py +77 -0
- sftp_helper-2.2.3/tests/test_cli.py +130 -0
- sftp_helper-2.2.3/tests/test_mcp.py +39 -0
- sftp_helper-2.2.3/tests/test_sftp_helper.py +406 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Warith HARCHAOUI
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sftp-helper
|
|
3
|
+
Version: 2.2.3
|
|
4
|
+
Summary: SFTP Helper โ utility functions for working with SFTP servers via paramiko, exposed as library, argparse CLI, click CLI, FastAPI HTTP surface and MCP tools. Strict host-key verification, credentials loader, upload / download / list / remove / exists / remote_tempfile context manager.
|
|
5
|
+
Author-email: Warith HARCHAOUI <warithmetics@deraison.ai>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/warith-harchaoui/sftp-helper
|
|
8
|
+
Project-URL: Issues, https://github.com/warith-harchaoui/sftp-helper/issues
|
|
9
|
+
Keywords: sftp,paramiko,ssh,file-transfer,secure
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Internet :: File Transfer Protocol (FTP)
|
|
20
|
+
Classifier: Topic :: System :: Networking
|
|
21
|
+
Requires-Python: <3.14,>=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: os-helper>=1.5.0
|
|
25
|
+
Requires-Dist: paramiko<4,>=3.0
|
|
26
|
+
Provides-Extra: cli
|
|
27
|
+
Requires-Dist: click<9,>=8.1; extra == "cli"
|
|
28
|
+
Provides-Extra: api
|
|
29
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "api"
|
|
30
|
+
Requires-Dist: uvicorn<1,>=0.30; extra == "api"
|
|
31
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "api"
|
|
32
|
+
Provides-Extra: mcp
|
|
33
|
+
Requires-Dist: fastapi-mcp<1,>=0.3; extra == "mcp"
|
|
34
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "mcp"
|
|
35
|
+
Requires-Dist: uvicorn<1,>=0.30; extra == "mcp"
|
|
36
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "mcp"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
39
|
+
Requires-Dist: pyyaml>=6; extra == "dev"
|
|
40
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
41
|
+
Requires-Dist: click<9,>=8.1; extra == "dev"
|
|
42
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "dev"
|
|
43
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "dev"
|
|
44
|
+
Requires-Dist: fastapi-mcp<1,>=0.3; extra == "dev"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# SFTP Helper
|
|
48
|
+
|
|
49
|
+
[๐ซ๐ท](https://github.com/warith-harchaoui/sftp-helper/blob/main/LISEZMOI.md) ยท [๐ฌ๐ง](https://github.com/warith-harchaoui/sftp-helper/blob/main/README.md)
|
|
50
|
+
|
|
51
|
+
[](https://github.com/warith-harchaoui/sftp-helper/actions/workflows/ci.yml) [](LICENSE) [](#)
|
|
52
|
+
|
|
53
|
+
`SFTP Helper` belongs to a collection of libraries called `AI Helpers` developped for building Artificial Intelligence
|
|
54
|
+
|
|
55
|
+
This toolbox requires:
|
|
56
|
+
- a `config.json` for the sftp parameters (or YAML or environment variables or .env)
|
|
57
|
+
- that you previously added you SSH key of your local machine in the SFTP server
|
|
58
|
+
|
|
59
|
+
[๐ AI Helpers](https://harchaoui.org/warith/ai-helpers)
|
|
60
|
+
|
|
61
|
+
[](https://harchaoui.org/warith/ai-helpers)
|
|
62
|
+
|
|
63
|
+
SFTP Helper is a Python library that provides utility functions for working with SFTP servers via [paramiko](https://www.paramiko.org/). Host key verification is on by default โ `~/.ssh/known_hosts` is loaded and unknown hosts are rejected.
|
|
64
|
+
|
|
65
|
+
# Installation
|
|
66
|
+
|
|
67
|
+
**Prerequisites** โ **Python 3.10โ3.13** and **git**, cross-platform:
|
|
68
|
+
|
|
69
|
+
- ๐ **macOS** ([Homebrew](https://brew.sh)): `brew install python git`
|
|
70
|
+
- ๐ง **Ubuntu/Debian**: `sudo apt update && sudo apt install -y python3 python3-pip git`
|
|
71
|
+
- ๐ช **Windows** (PowerShell): `winget install Python.Python.3.12 Git.Git`
|
|
72
|
+
|
|
73
|
+
Then install the package:
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Install Package
|
|
77
|
+
|
|
78
|
+
We can recommand python environments. Check this link if you don't know how
|
|
79
|
+
|
|
80
|
+
[๐ฅธ Tech tips](https://harchaoui.org/warith/4ml/#install)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install --force-reinstall --no-cache-dir git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or, from a checkout:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install -r requirements.txt
|
|
90
|
+
pip install -e .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Write your own configuration file
|
|
94
|
+
|
|
95
|
+
A ready-to-fill template is committed at [`sftp_config.json.example`](https://github.com/warith-harchaoui/sftp-helper/blob/main/sftp_config.json.example). A heavily-commented YAML variant is also provided at [`sftp_config.yaml.example`](https://github.com/warith-harchaoui/sftp-helper/blob/main/sftp_config.yaml.example) โ YAML supports inline comments explaining every key and how to obtain its value. Copy either one and edit in place โ real `*config.json` / `*config.yaml` files are gitignored so you cannot accidentally commit secrets:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cp sftp_config.json.example sftp_config.json
|
|
99
|
+
# then edit sftp_config.json with your credentials
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
You may also provide a YAML version (`sftp_config.yaml`), environment variables, or an `.env` file โ `sftp-helper` falls back in that order via `os_helper.get_config`:
|
|
103
|
+
|
|
104
|
+
_JSON_
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"sftp_host": "<sftp_host>",
|
|
108
|
+
"sftp_login": "<sftp_login>",
|
|
109
|
+
"sftp_passwd": "<sftp_passwd>",
|
|
110
|
+
"sftp_https": "<sftp_https>",
|
|
111
|
+
"sftp_destination_path": "<sftp_destination_path>",
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
or
|
|
115
|
+
|
|
116
|
+
_YAML_
|
|
117
|
+
```yaml
|
|
118
|
+
sftp_host: "<sftp_host>"
|
|
119
|
+
sftp_login: "<sftp_login>"
|
|
120
|
+
sftp_passwd: "<sftp_passwd>"
|
|
121
|
+
sftp_https: "<sftp_https>"
|
|
122
|
+
sftp_destination_path: "<sftp_destination_path>"
|
|
123
|
+
```
|
|
124
|
+
or
|
|
125
|
+
|
|
126
|
+
_ENVIRONMENT VARIABLES_
|
|
127
|
+
```bash
|
|
128
|
+
SFTP_HOST="<sftp_host>" \
|
|
129
|
+
SFTP_LOGIN="<sftp_login>" \
|
|
130
|
+
SFTP_PASSWD="<sftp_passwd>" \
|
|
131
|
+
SFTP_HTTPS="<sftp_https>" \
|
|
132
|
+
SFTP_DESTINATION_PATH="<sftp_destination_path>" \
|
|
133
|
+
python <your_python_script>
|
|
134
|
+
```
|
|
135
|
+
or
|
|
136
|
+
|
|
137
|
+
_.env_
|
|
138
|
+
```
|
|
139
|
+
SFTP_HOST = <sftp_host>
|
|
140
|
+
SFTP_LOGIN = <sftp_login>
|
|
141
|
+
SFTP_PASSWD = <sftp_passwd>
|
|
142
|
+
SFTP_HTTPS = <sftp_https>
|
|
143
|
+
SFTP_DESTINATION_PATH = <sftp_destination_path>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
In which you can find these information in your favorite FTP tool (mine is FileZilla):
|
|
147
|
+
+ `<sftp_host>` is the server path `sftp.` ...
|
|
148
|
+
+ `<sftp_login>` and `<sftp_passwd>` that you use in FileZilla
|
|
149
|
+
+ `<sftp_destination_path>` is the remote folder path
|
|
150
|
+
+ `<sftp_https>` corresponds to the web URL of `<sftp_destination_path>`
|
|
151
|
+
+ <your_python_script> is your python script :)
|
|
152
|
+
|
|
153
|
+
## Usage
|
|
154
|
+
|
|
155
|
+
For the full catalog of recipes (uploads, downloads, existence checks, recursive directory creation, temporary remote files with auto-cleanup, strict host-key verification), see [๐ EXAMPLES.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/EXAMPLES.md).
|
|
156
|
+
|
|
157
|
+
Here's an example of how to use SFTP helper (**won't work without a valid `path/to/sftp_config.json`**):
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
import sftp_helper as sftph
|
|
161
|
+
import os_helper as osh
|
|
162
|
+
|
|
163
|
+
# Write a small text file
|
|
164
|
+
local_file = "example.txt"
|
|
165
|
+
with open(local_file, "wt") as f:
|
|
166
|
+
f.write("A small example of text")
|
|
167
|
+
|
|
168
|
+
# Load creds from JSON / YAML file, or fall back to .env / environment vars.
|
|
169
|
+
cred = sftph.credentials("path/to/sftp_config.json")
|
|
170
|
+
|
|
171
|
+
remote_file = cred["sftp_destination_path"] + "/" + local_file
|
|
172
|
+
url = cred["sftp_https"] + "/" + local_file
|
|
173
|
+
|
|
174
|
+
# upload() raises on failure and returns the destination URL on success.
|
|
175
|
+
sftph.upload(local_file, cred, remote_file)
|
|
176
|
+
print(f"Uploaded {local_file} to {remote_file}")
|
|
177
|
+
# Uploaded example.txt to /remote/base/path/example.txt
|
|
178
|
+
|
|
179
|
+
assert osh.is_working_url(url), f"URL not reachable: {url}"
|
|
180
|
+
print(f"URL is live: {url}")
|
|
181
|
+
# URL is live: https://files.example.com/example.txt
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Temporary remote files
|
|
185
|
+
|
|
186
|
+
If you need a unique remote path that gets cleaned up automatically, use the
|
|
187
|
+
`remote_tempfile` context manager:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import sftp_helper as sftph
|
|
191
|
+
import os_helper as osh
|
|
192
|
+
|
|
193
|
+
credentials = sftph.credentials("path/to/sftp_config.json")
|
|
194
|
+
|
|
195
|
+
with sftph.remote_tempfile(credentials, ext="txt") as (sftp_address, url):
|
|
196
|
+
sftph.upload("local.txt", credentials, sftp_address)
|
|
197
|
+
assert osh.is_working_url(url)
|
|
198
|
+
# On exit, the remote file is deleted.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Host key verification
|
|
202
|
+
|
|
203
|
+
`sftp_helper` never disables host key verification. The default policy is
|
|
204
|
+
`paramiko.RejectPolicy()` and `~/.ssh/known_hosts` is loaded automatically. To
|
|
205
|
+
trust a server in a non-default location, point at an extra known_hosts file
|
|
206
|
+
via the optional `sftp_known_hosts` credential.
|
|
207
|
+
|
|
208
|
+
# Multi-surface exposure
|
|
209
|
+
|
|
210
|
+
`sftp-helper` is not just a library โ the same functions are exposed
|
|
211
|
+
as a CLI, a FastAPI HTTP surface, and an MCP tool set:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Python library (default)
|
|
215
|
+
import sftp_helper as sftph
|
|
216
|
+
|
|
217
|
+
# argparse-based CLI (installed automatically)
|
|
218
|
+
sftp-helper upload --config sftp_config.json --input local.txt --remote /uploads/local.txt
|
|
219
|
+
sftp-helper download --config sftp_config.json --remote /uploads/local.txt --output out.txt
|
|
220
|
+
sftp-helper exists --config sftp_config.json --remote /uploads/local.txt
|
|
221
|
+
sftp-helper mkdir --config sftp_config.json --remote /uploads/a/b/c
|
|
222
|
+
|
|
223
|
+
# click-based CLI twin (needs the [cli] extra)
|
|
224
|
+
pip install 'sftp-helper[cli] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
225
|
+
sftp-helper-click upload --config sftp_config.json --input local.txt --remote /uploads/local.txt
|
|
226
|
+
|
|
227
|
+
# FastAPI HTTP surface (needs the [api] extra)
|
|
228
|
+
pip install 'sftp-helper[api] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
229
|
+
SFTP_HELPER_CONFIG=./sftp_config.json uvicorn sftp_helper.api:app --port 8000
|
|
230
|
+
# โ OpenAPI docs at http://localhost:8000/docs
|
|
231
|
+
|
|
232
|
+
# MCP tools over FastAPI (needs the [api,mcp] extras)
|
|
233
|
+
pip install 'sftp-helper[api,mcp] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
234
|
+
sftp-helper-mcp # serves FastAPI + MCP on port 8000
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Docker image (HTTP + MCP on port 8000):
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
docker build -t sftp-helper .
|
|
241
|
+
docker run --rm -p 8000:8000 \
|
|
242
|
+
-v $PWD/sftp_config.json:/app/sftp_config.json:ro \
|
|
243
|
+
-e SFTP_HELPER_CONFIG=/app/sftp_config.json \
|
|
244
|
+
sftp-helper
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
An innovative GUI plan (pipeline dashboard, storage health panel,
|
|
248
|
+
live transfer feed) lives in [GUI.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/GUI.md).
|
|
249
|
+
|
|
250
|
+
The competitive landscape (paramiko, pysftp, asyncssh, Fabric,
|
|
251
|
+
smart-open, PyFilesystem2, lftp, Rclone, โฆ) is analysed in
|
|
252
|
+
[LANDSCAPE.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/LANDSCAPE.md).
|
|
253
|
+
|
|
254
|
+
# Author
|
|
255
|
+
- [Warith HARCHAOUI](https://linkedin.com/in/warith-harchaoui)
|
|
256
|
+
|
|
257
|
+
# Acknowledgements
|
|
258
|
+
Special thanks to [Mohamed Chelali](https://mchelali.github.io) and [Bachir Zerroug](https://www.linkedin.com/in/bachirzerroug) for fruitful discussions.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# SFTP Helper
|
|
2
|
+
|
|
3
|
+
[๐ซ๐ท](https://github.com/warith-harchaoui/sftp-helper/blob/main/LISEZMOI.md) ยท [๐ฌ๐ง](https://github.com/warith-harchaoui/sftp-helper/blob/main/README.md)
|
|
4
|
+
|
|
5
|
+
[](https://github.com/warith-harchaoui/sftp-helper/actions/workflows/ci.yml) [](LICENSE) [](#)
|
|
6
|
+
|
|
7
|
+
`SFTP Helper` belongs to a collection of libraries called `AI Helpers` developped for building Artificial Intelligence
|
|
8
|
+
|
|
9
|
+
This toolbox requires:
|
|
10
|
+
- a `config.json` for the sftp parameters (or YAML or environment variables or .env)
|
|
11
|
+
- that you previously added you SSH key of your local machine in the SFTP server
|
|
12
|
+
|
|
13
|
+
[๐ AI Helpers](https://harchaoui.org/warith/ai-helpers)
|
|
14
|
+
|
|
15
|
+
[](https://harchaoui.org/warith/ai-helpers)
|
|
16
|
+
|
|
17
|
+
SFTP Helper is a Python library that provides utility functions for working with SFTP servers via [paramiko](https://www.paramiko.org/). Host key verification is on by default โ `~/.ssh/known_hosts` is loaded and unknown hosts are rejected.
|
|
18
|
+
|
|
19
|
+
# Installation
|
|
20
|
+
|
|
21
|
+
**Prerequisites** โ **Python 3.10โ3.13** and **git**, cross-platform:
|
|
22
|
+
|
|
23
|
+
- ๐ **macOS** ([Homebrew](https://brew.sh)): `brew install python git`
|
|
24
|
+
- ๐ง **Ubuntu/Debian**: `sudo apt update && sudo apt install -y python3 python3-pip git`
|
|
25
|
+
- ๐ช **Windows** (PowerShell): `winget install Python.Python.3.12 Git.Git`
|
|
26
|
+
|
|
27
|
+
Then install the package:
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Install Package
|
|
31
|
+
|
|
32
|
+
We can recommand python environments. Check this link if you don't know how
|
|
33
|
+
|
|
34
|
+
[๐ฅธ Tech tips](https://harchaoui.org/warith/4ml/#install)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install --force-reinstall --no-cache-dir git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or, from a checkout:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install -r requirements.txt
|
|
44
|
+
pip install -e .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Write your own configuration file
|
|
48
|
+
|
|
49
|
+
A ready-to-fill template is committed at [`sftp_config.json.example`](https://github.com/warith-harchaoui/sftp-helper/blob/main/sftp_config.json.example). A heavily-commented YAML variant is also provided at [`sftp_config.yaml.example`](https://github.com/warith-harchaoui/sftp-helper/blob/main/sftp_config.yaml.example) โ YAML supports inline comments explaining every key and how to obtain its value. Copy either one and edit in place โ real `*config.json` / `*config.yaml` files are gitignored so you cannot accidentally commit secrets:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cp sftp_config.json.example sftp_config.json
|
|
53
|
+
# then edit sftp_config.json with your credentials
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You may also provide a YAML version (`sftp_config.yaml`), environment variables, or an `.env` file โ `sftp-helper` falls back in that order via `os_helper.get_config`:
|
|
57
|
+
|
|
58
|
+
_JSON_
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"sftp_host": "<sftp_host>",
|
|
62
|
+
"sftp_login": "<sftp_login>",
|
|
63
|
+
"sftp_passwd": "<sftp_passwd>",
|
|
64
|
+
"sftp_https": "<sftp_https>",
|
|
65
|
+
"sftp_destination_path": "<sftp_destination_path>",
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
or
|
|
69
|
+
|
|
70
|
+
_YAML_
|
|
71
|
+
```yaml
|
|
72
|
+
sftp_host: "<sftp_host>"
|
|
73
|
+
sftp_login: "<sftp_login>"
|
|
74
|
+
sftp_passwd: "<sftp_passwd>"
|
|
75
|
+
sftp_https: "<sftp_https>"
|
|
76
|
+
sftp_destination_path: "<sftp_destination_path>"
|
|
77
|
+
```
|
|
78
|
+
or
|
|
79
|
+
|
|
80
|
+
_ENVIRONMENT VARIABLES_
|
|
81
|
+
```bash
|
|
82
|
+
SFTP_HOST="<sftp_host>" \
|
|
83
|
+
SFTP_LOGIN="<sftp_login>" \
|
|
84
|
+
SFTP_PASSWD="<sftp_passwd>" \
|
|
85
|
+
SFTP_HTTPS="<sftp_https>" \
|
|
86
|
+
SFTP_DESTINATION_PATH="<sftp_destination_path>" \
|
|
87
|
+
python <your_python_script>
|
|
88
|
+
```
|
|
89
|
+
or
|
|
90
|
+
|
|
91
|
+
_.env_
|
|
92
|
+
```
|
|
93
|
+
SFTP_HOST = <sftp_host>
|
|
94
|
+
SFTP_LOGIN = <sftp_login>
|
|
95
|
+
SFTP_PASSWD = <sftp_passwd>
|
|
96
|
+
SFTP_HTTPS = <sftp_https>
|
|
97
|
+
SFTP_DESTINATION_PATH = <sftp_destination_path>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
In which you can find these information in your favorite FTP tool (mine is FileZilla):
|
|
101
|
+
+ `<sftp_host>` is the server path `sftp.` ...
|
|
102
|
+
+ `<sftp_login>` and `<sftp_passwd>` that you use in FileZilla
|
|
103
|
+
+ `<sftp_destination_path>` is the remote folder path
|
|
104
|
+
+ `<sftp_https>` corresponds to the web URL of `<sftp_destination_path>`
|
|
105
|
+
+ <your_python_script> is your python script :)
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
For the full catalog of recipes (uploads, downloads, existence checks, recursive directory creation, temporary remote files with auto-cleanup, strict host-key verification), see [๐ EXAMPLES.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/EXAMPLES.md).
|
|
110
|
+
|
|
111
|
+
Here's an example of how to use SFTP helper (**won't work without a valid `path/to/sftp_config.json`**):
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import sftp_helper as sftph
|
|
115
|
+
import os_helper as osh
|
|
116
|
+
|
|
117
|
+
# Write a small text file
|
|
118
|
+
local_file = "example.txt"
|
|
119
|
+
with open(local_file, "wt") as f:
|
|
120
|
+
f.write("A small example of text")
|
|
121
|
+
|
|
122
|
+
# Load creds from JSON / YAML file, or fall back to .env / environment vars.
|
|
123
|
+
cred = sftph.credentials("path/to/sftp_config.json")
|
|
124
|
+
|
|
125
|
+
remote_file = cred["sftp_destination_path"] + "/" + local_file
|
|
126
|
+
url = cred["sftp_https"] + "/" + local_file
|
|
127
|
+
|
|
128
|
+
# upload() raises on failure and returns the destination URL on success.
|
|
129
|
+
sftph.upload(local_file, cred, remote_file)
|
|
130
|
+
print(f"Uploaded {local_file} to {remote_file}")
|
|
131
|
+
# Uploaded example.txt to /remote/base/path/example.txt
|
|
132
|
+
|
|
133
|
+
assert osh.is_working_url(url), f"URL not reachable: {url}"
|
|
134
|
+
print(f"URL is live: {url}")
|
|
135
|
+
# URL is live: https://files.example.com/example.txt
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Temporary remote files
|
|
139
|
+
|
|
140
|
+
If you need a unique remote path that gets cleaned up automatically, use the
|
|
141
|
+
`remote_tempfile` context manager:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import sftp_helper as sftph
|
|
145
|
+
import os_helper as osh
|
|
146
|
+
|
|
147
|
+
credentials = sftph.credentials("path/to/sftp_config.json")
|
|
148
|
+
|
|
149
|
+
with sftph.remote_tempfile(credentials, ext="txt") as (sftp_address, url):
|
|
150
|
+
sftph.upload("local.txt", credentials, sftp_address)
|
|
151
|
+
assert osh.is_working_url(url)
|
|
152
|
+
# On exit, the remote file is deleted.
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Host key verification
|
|
156
|
+
|
|
157
|
+
`sftp_helper` never disables host key verification. The default policy is
|
|
158
|
+
`paramiko.RejectPolicy()` and `~/.ssh/known_hosts` is loaded automatically. To
|
|
159
|
+
trust a server in a non-default location, point at an extra known_hosts file
|
|
160
|
+
via the optional `sftp_known_hosts` credential.
|
|
161
|
+
|
|
162
|
+
# Multi-surface exposure
|
|
163
|
+
|
|
164
|
+
`sftp-helper` is not just a library โ the same functions are exposed
|
|
165
|
+
as a CLI, a FastAPI HTTP surface, and an MCP tool set:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Python library (default)
|
|
169
|
+
import sftp_helper as sftph
|
|
170
|
+
|
|
171
|
+
# argparse-based CLI (installed automatically)
|
|
172
|
+
sftp-helper upload --config sftp_config.json --input local.txt --remote /uploads/local.txt
|
|
173
|
+
sftp-helper download --config sftp_config.json --remote /uploads/local.txt --output out.txt
|
|
174
|
+
sftp-helper exists --config sftp_config.json --remote /uploads/local.txt
|
|
175
|
+
sftp-helper mkdir --config sftp_config.json --remote /uploads/a/b/c
|
|
176
|
+
|
|
177
|
+
# click-based CLI twin (needs the [cli] extra)
|
|
178
|
+
pip install 'sftp-helper[cli] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
179
|
+
sftp-helper-click upload --config sftp_config.json --input local.txt --remote /uploads/local.txt
|
|
180
|
+
|
|
181
|
+
# FastAPI HTTP surface (needs the [api] extra)
|
|
182
|
+
pip install 'sftp-helper[api] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
183
|
+
SFTP_HELPER_CONFIG=./sftp_config.json uvicorn sftp_helper.api:app --port 8000
|
|
184
|
+
# โ OpenAPI docs at http://localhost:8000/docs
|
|
185
|
+
|
|
186
|
+
# MCP tools over FastAPI (needs the [api,mcp] extras)
|
|
187
|
+
pip install 'sftp-helper[api,mcp] @ git+https://github.com/warith-harchaoui/sftp-helper.git@v2.2.2'
|
|
188
|
+
sftp-helper-mcp # serves FastAPI + MCP on port 8000
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Docker image (HTTP + MCP on port 8000):
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
docker build -t sftp-helper .
|
|
195
|
+
docker run --rm -p 8000:8000 \
|
|
196
|
+
-v $PWD/sftp_config.json:/app/sftp_config.json:ro \
|
|
197
|
+
-e SFTP_HELPER_CONFIG=/app/sftp_config.json \
|
|
198
|
+
sftp-helper
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
An innovative GUI plan (pipeline dashboard, storage health panel,
|
|
202
|
+
live transfer feed) lives in [GUI.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/GUI.md).
|
|
203
|
+
|
|
204
|
+
The competitive landscape (paramiko, pysftp, asyncssh, Fabric,
|
|
205
|
+
smart-open, PyFilesystem2, lftp, Rclone, โฆ) is analysed in
|
|
206
|
+
[LANDSCAPE.md](https://github.com/warith-harchaoui/sftp-helper/blob/main/LANDSCAPE.md).
|
|
207
|
+
|
|
208
|
+
# Author
|
|
209
|
+
- [Warith HARCHAOUI](https://linkedin.com/in/warith-harchaoui)
|
|
210
|
+
|
|
211
|
+
# Acknowledgements
|
|
212
|
+
Special thanks to [Mohamed Chelali](https://mchelali.github.io) and [Bachir Zerroug](https://www.linkedin.com/in/bachirzerroug) for fruitful discussions.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sftp-helper"
|
|
7
|
+
version = "2.2.3"
|
|
8
|
+
description = "SFTP Helper โ utility functions for working with SFTP servers via paramiko, exposed as library, argparse CLI, click CLI, FastAPI HTTP surface and MCP tools. Strict host-key verification, credentials loader, upload / download / list / remove / exists / remote_tempfile context manager."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10,<3.14"
|
|
11
|
+
license = {text = "BSD-3-Clause"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Warith HARCHAOUI", email = "warithmetics@deraison.ai"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["sftp", "paramiko", "ssh", "file-transfer", "secure"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: BSD License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Internet :: File Transfer Protocol (FTP)",
|
|
27
|
+
"Topic :: System :: Networking",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"os-helper>=1.5.0",
|
|
31
|
+
"paramiko>=3.0,<4",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
# CLI-only-with-click extra. argparse variant needs no runtime dep and
|
|
36
|
+
# is always available.
|
|
37
|
+
cli = ["click>=8.1,<9"]
|
|
38
|
+
# FastAPI HTTP surface (sftp_helper.api). uvicorn + python-multipart
|
|
39
|
+
# so multipart file uploads work out of the box.
|
|
40
|
+
api = [
|
|
41
|
+
"fastapi>=0.111,<1",
|
|
42
|
+
"uvicorn>=0.30,<1",
|
|
43
|
+
"python-multipart>=0.0.9,<1",
|
|
44
|
+
]
|
|
45
|
+
# MCP surface (sftp_helper.mcp). Depends on the `api` extra
|
|
46
|
+
# โ fastapi-mcp wraps the FastAPI app.
|
|
47
|
+
mcp = [
|
|
48
|
+
"fastapi-mcp>=0.3,<1",
|
|
49
|
+
"fastapi>=0.111,<1",
|
|
50
|
+
"uvicorn>=0.30,<1",
|
|
51
|
+
"python-multipart>=0.0.9,<1",
|
|
52
|
+
]
|
|
53
|
+
# `dev` keeps the bar low for CI โ pytest + httpx (for FastAPI TestClient)
|
|
54
|
+
# + click (so the click CLI import always resolves in tests) + pyyaml
|
|
55
|
+
# (used by the credentials tests to write YAML configs).
|
|
56
|
+
dev = [
|
|
57
|
+
"pytest>=8",
|
|
58
|
+
"pyyaml>=6",
|
|
59
|
+
"httpx>=0.27",
|
|
60
|
+
"click>=8.1,<9",
|
|
61
|
+
"fastapi>=0.111,<1",
|
|
62
|
+
"python-multipart>=0.0.9,<1",
|
|
63
|
+
"fastapi-mcp>=0.3,<1",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[project.scripts]
|
|
67
|
+
# argparse-based CLI โ no runtime dep beyond stdlib + core package.
|
|
68
|
+
sftp-helper = "sftp_helper.cli_argparse:main"
|
|
69
|
+
# click-based twin โ install with [cli] extra.
|
|
70
|
+
sftp-helper-click = "sftp_helper.cli_click:cli"
|
|
71
|
+
# MCP server (over FastAPI) โ install with [api,mcp] extras.
|
|
72
|
+
sftp-helper-mcp = "sftp_helper.mcp:main"
|
|
73
|
+
|
|
74
|
+
[project.urls]
|
|
75
|
+
Homepage = "https://github.com/warith-harchaoui/sftp-helper"
|
|
76
|
+
Issues = "https://github.com/warith-harchaoui/sftp-helper/issues"
|
|
77
|
+
|
|
78
|
+
[tool.setuptools.packages.find]
|
|
79
|
+
include = ["sftp_helper*"]
|
|
80
|
+
exclude = ["tests*", "assets*"]
|
|
81
|
+
|
|
82
|
+
[tool.pytest.ini_options]
|
|
83
|
+
markers = [
|
|
84
|
+
"integration: tests that hit a real SFTP server (skipped by default)",
|
|
85
|
+
]
|
|
86
|
+
addopts = "-m 'not integration'"
|
|
87
|
+
|
|
88
|
+
[tool.ruff]
|
|
89
|
+
line-length = 100
|
|
90
|
+
target-version = "py310"
|
|
91
|
+
extend-exclude = ["assets", "tests"]
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
# Conservative starter set: pyflakes (F), pycodestyle errors (E), warnings (W),
|
|
95
|
+
# isort (I), bugbear (B), comprehensions (C4), pyupgrade (UP), simplify (SIM).
|
|
96
|
+
# Easy to tighten later; this is the baseline.
|
|
97
|
+
select = ["E", "F", "W", "I", "B", "C4", "UP", "SIM"]
|
|
98
|
+
ignore = [
|
|
99
|
+
"E501", # line-too-long โ handled by formatter / context-dependent
|
|
100
|
+
"B008", # function call in argument default โ too noisy for our style
|
|
101
|
+
"SIM108", # ternary preferred over if/else โ sometimes hurts readability
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.lint.per-file-ignores]
|
|
105
|
+
"tests/*" = ["B011"] # assert False in pytest is fine
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SFTP Helper โ public API surface.
|
|
3
|
+
|
|
4
|
+
Re-exports the utility functions from :mod:`sftp_helper.main` so that
|
|
5
|
+
downstream code can simply write ``import sftp_helper as sftph`` and reach
|
|
6
|
+
every supported operation (credentials loading, connection context manager,
|
|
7
|
+
upload, download, exists, delete, mkdir -p, remote temp file with
|
|
8
|
+
auto-cleanup) without knowing about the module layout.
|
|
9
|
+
|
|
10
|
+
Backed by paramiko with strict host-key verification. See the module docs
|
|
11
|
+
for the full policy โ there is no flag to disable verification.
|
|
12
|
+
|
|
13
|
+
Usage Example
|
|
14
|
+
-------------
|
|
15
|
+
>>> import sftp_helper as sftph
|
|
16
|
+
>>> cred = sftph.credentials("sftp_config.json")
|
|
17
|
+
>>> sftph.upload("local.txt", cred, "/remote/base/local.txt")
|
|
18
|
+
>>> assert sftph.remote_file_exists("/remote/base/local.txt", cred)
|
|
19
|
+
>>> sftph.download("/remote/base/local.txt", cred, "roundtrip.txt")
|
|
20
|
+
>>> sftph.delete("/remote/base/local.txt", cred)
|
|
21
|
+
|
|
22
|
+
Author
|
|
23
|
+
------
|
|
24
|
+
Warith Harchaoui, Ph.D. โ https://linkedin.com/in/warith-harchaoui/
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__author__ = "Warith Harchaoui, Ph.D."
|
|
28
|
+
__email__ = "warithmetics@deraison.ai"
|
|
29
|
+
|
|
30
|
+
# Specify the public API of this module using __all__
|
|
31
|
+
__all__ = [
|
|
32
|
+
"credentials",
|
|
33
|
+
"get_client_sftp",
|
|
34
|
+
"normalize_path",
|
|
35
|
+
"strip_sftp_path",
|
|
36
|
+
"remote_file_exists",
|
|
37
|
+
"delete",
|
|
38
|
+
"upload",
|
|
39
|
+
"download",
|
|
40
|
+
"remote_dir_exist",
|
|
41
|
+
"make_remote_directory",
|
|
42
|
+
"remote_tempfile",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
from .main import (
|
|
46
|
+
credentials,
|
|
47
|
+
delete,
|
|
48
|
+
download,
|
|
49
|
+
get_client_sftp,
|
|
50
|
+
make_remote_directory,
|
|
51
|
+
normalize_path,
|
|
52
|
+
remote_dir_exist,
|
|
53
|
+
remote_file_exists,
|
|
54
|
+
remote_tempfile,
|
|
55
|
+
strip_sftp_path,
|
|
56
|
+
upload,
|
|
57
|
+
)
|