dagster-sftp
A Dagster integration for interacting with SFTP servers, providing a high-performance resource for file transfer operations with support for parallel transfers, batch operations, and advanced filtering capabilities.
Features
- High-Performance Transfers: Automatic parallelization of large file transfers using asyncSSH
- Batch Operations: Efficient concurrent processing of multiple files
- Advanced Filtering: Pattern matching with glob, date-based filtering, and file type filtering
- Progress Monitoring: Built-in progress callbacks for tracking transfer status
- Flexible Authentication: Support for both password and key-based authentication
- Comprehensive File Operations: Upload, download, delete, rename, and directory management
- Type-Safe: Full type hints and Pydantic configuration
Installation
pip install dagster-sftp
Quick Start
Basic Configuration
from dagster import Definitions, asset
from dagster_sftp import SFTPResource
# Password authentication
sftp_resource = SFTPResource(
host="sftp.example.com",
username="myuser",
password="mypassword",
port=22, # optional, defaults to 22
)
# Key-based authentication
sftp_resource = SFTPResource(
host="sftp.example.com",
username="myuser",
private_key="/path/to/private/key",
passphrase="key_passphrase", # optional
known_hosts="/path/to/known_hosts" # optional
)
defs = Definitions(
resources={
"sftp": sftp_resource
}
)
Basic File Operations
from dagster import asset
from dagster_sftp import SFTPResource
@asset
def download_files(sftp: SFTPResource):
"""Download a file from SFTP server."""
# Download a single file
sftp.get_file(
remote_path="/data/report.csv",
local_path="/tmp/report.csv"
)
# Upload a file
sftp.put_file(
local_path="/tmp/processed_report.csv",
remote_path="/data/processed/report.csv"
)
# Delete a file
sftp.delete_file("/data/old_report.csv")
return {"status": "Files processed successfully"}
Complete Example: Sensor for new files + Asset for processing
from datetime import datetime, timedelta
import dagster as dg
import tempfile
from dagster_sftp import SFTPResource, SFTPFileInfo, SFTPFileInfoConfig
@dg.asset
def my_sftp_asset(context: dg.AssetExecutionContext, config: SFTPFileInfoConfig ,sftp: SFTPResource):
"""Demonstrate SFTP asset usage."""
context.log.info(f"Processing file {config.path}")
with tempfile.NamedTemporaryFile() as tmp_file:
sftp.get_file(config.path, tmp_file.name)
# Process the file...
@dg.sensor(target=my_sftp_asset)
def sftp_file_sensor(context: dg.SensorEvaluationContext, sftp: dg.SFTPResource):
"""Detect new files on SFTP server and trigger processing."""
last_check = datetime.fromisoformat(context.cursor) if context.cursor else None
current_check = datetime.now()
try:
new_files = sftp.list_files(
base_path="/incoming",
pattern="*.csv",
files_only=True,
modified_after=last_check
)
if not new_files:
return dg.SkipReason(f"No new files found since {last_check.isoformat()}")
return dg.SensorResult(
run_requests=[
dg.RunRequest(
asset_selection=[my_sftp_asset.key],
run_key=file.id,
run_config=dg.RunConfig(
ops={my_sftp_asset.key.to_python_identifier(): {"config": file.to_config_dict()}}
),
) for file in new_files
],
cursor=current_check.isoformat(),
)
except Exception as e:
context.log.error(f"Error checking SFTP: {str(e)}")
return dg.Failure(f"Error checking SFTP: {str(e)}")
File Listing with Filtering
import dagster as dg
from datetime import datetime, timedelta
from dagster_sftp import SFTPResource
@dg.asset
def process_recent_files(sftp: SFTPResource):
"""Process files modified in the last week."""
# List CSV files modified in the last 7 days
recent_files = sftp.list_files(
base_path="/data/exports",
pattern="*.csv",
files_only=True,
modified_after=datetime.now() - timedelta(days=7)
)
for file_info in recent_files:
print(f"Processing {file_info.filename}")
print(f" Size: {file_info.size} bytes")
print(f" Modified: {file_info.modified_time}")
# Download and process each file
local_path = f"/tmp/{file_info.filename}"
sftp.get_file(file_info.path, local_path)
# Process the file...
return {"files_processed": len(recent_files)}
Directory Operations
import dagster as dg
from dagster_sftp import SFTPResource
@dg.op
def manage_directories(context: dg.OpExecutionContext, sftp: SFTPResource):
"""Create and manage directory structures."""
# Create directory structure (including parents)
sftp.mkdir("/remote/data/2024/reports", mode=0o755)
# Check if path is directory
if sftp.is_dir("/remote/data"):
# Recursively list all Python files
python_files = sftp.list_files(
base_path="/remote/data",
pattern="**/*.py", # Recursive search
files_only=True
)
# Move/rename directories
sftp.rename(
old_path="/remote/old_reports",
new_path="/remote/archived_reports"
)
# Remove directory tree (use with caution!)
if sftp.file_exists("/remote/temp"):
context.log.warning("Removing /remote/temp...")
sftp.rmtree("/remote/temp")
return {"directories": "managed"}
Performance Tips
The SFTP resource uses asyncSSH internally for optimal performance:
- Parallel Transfers: Large files are automatically split into chunks and transferred in parallel
- Concurrent Operations: Batch operations (like deleting multiple files) are performed concurrently
- Connection Pooling: Efficiently manages SFTP connections
- Configurable Parallelization: Tune
max_requestsandblock_sizefor your network conditions
# For large files on fast networks
sftp.get_file(
remote_path="/data/large_file.zip",
local_path="/tmp/large_file.zip",
max_requests=256, # Increase parallel chunks
block_size=131072 # 128KB blocks
)
# For many small files
files = [f"/data/file_{i}.txt" for i in range(1000)]
sftp.delete_files(files) # Concurrent deletion, much faster than sequential
Configuration Reference
SFTPResource Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
host |
str | Required | SFTP server hostname or IP address |
port |
int | 22 | SFTP server port |
username |
str | Required | Username for authentication |
password |
str | None | Password for authentication |
private_key |
str | None | Path to private key file |
passphrase |
str | None | Passphrase for encrypted private key |
known_hosts |
str | None | Path to known_hosts file (None disables host key checking) |
keepalive_interval |
int | 15 | Seconds between keepalive packets |
connect_timeout |
int | 60 | Connection timeout in seconds |
default_max_requests |
int | -1 | Max concurrent SFTP requests (-1 for server default) |
default_block_size |
int | -1 | Block size for parallel transfers (-1 for server default) |
API Reference
Main Classes
SFTPResource
The main resource class for SFTP operations. All methods are synchronous but use asyncSSH internally for performance.
Key Methods:
list_files()- List and filter files with glob patternsget_file()- Download file(s) from serverput_file()- Upload file(s) to serverdelete_file()- Delete a single filedelete_files()- Delete multiple files concurrentlyfile_exists()- Check if a file/directory existsmkdir()- Create directory (with parents)rmtree()- Remove directory treeis_dir()- Check if path is a directoryis_file()- Check if path is a regular filerename()- Rename or move files/directoriesget_file_info()- Get detailed file metadata
FileInfo
Data class containing file/directory metadata returned by list_files() and get_file_info().
Attributes:
filename- Name of the filepath- Full path to the filesize- File size in bytesis_dir- True if directoryis_file- True if regular fileis_link- True if symbolic linkmode- Unix permission modemodified_time- Last modification timeaccessed_time- Last access time (optional)owner- Owner UID (optional)group- Group GID (optional)
Development
Testing
make test
Building
make build
Code Quality
# Format and lint
make ruff
# Type checking
make check
License
This integration is part of the Dagster Community Integrations project and follows the same licensing terms as the main repository
Metadata
Release files for dagster-sftp 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dagster_sftp-0.0.3.tar.gz | 17.0 kB | Without provenance | Details for dagster_sftp-0.0.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dagster_sftp-0.0.3-py3-none-any.whl | Python 3 | none | any | Details for dagster_sftp-0.0.3-py3-none-any.whl |
Total release size: 31.9 kB
Release files / dagster_sftp-0.0.3.tar.gz
| Download URL | dagster_sftp-0.0.3.tar.gz |
|---|---|
| Size | 17.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
41c1302272c856c8faa3e1fee33319b65c73214d838600a5df226d8ffd8fbe44
|
|
BLAKE2b-256 checksum How to use checksums |
34befc02156736411d6a6383ecd8da0a01f679fed17104ae966a9c82c64a185e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / dagster_sftp-0.0.3-py3-none-any.whl
| Download URL | dagster_sftp-0.0.3-py3-none-any.whl |
|---|---|
| Size | 14.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
53b5c7462db6dbfe691a5e6d8f097a2bd50ab97a009061cc06519e7c9091b525
|
|
BLAKE2b-256 checksum How to use checksums |
7906c0c64166bdf0dd608367695f0356c6dc5b7aecaa743ab2c640758972910c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for dagster-sftp 0.0.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.