Skip to content

Commit 3dbe93e

Browse files
authored
Merge pull request #7 from rohaquinlop/add-upload
Support Objects Upload
2 parents ad90dda + adace22 commit 3dbe93e

16 files changed

Lines changed: 1523 additions & 623 deletions

Cargo.lock

Lines changed: 134 additions & 112 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "robinzhon"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2021"
55
authors = ["Robin Quintero <rohaquinlop301@gmail.com>"]
66
license = "MIT"
@@ -20,5 +20,4 @@ aws-config = "1.8.3"
2020
aws-sdk-s3 = "1.100.0"
2121
futures = "0.3.31"
2222
pyo3 = "0.25.0"
23-
rayon = "1.10.0"
24-
tokio = "1.46.1"
23+
tokio = { version = "1.46.1", features = ["full"] }

README.md

Lines changed: 55 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,82 @@
11
# robinzhon
22

3-
A high-performance Python library for fast, concurrent S3 object downloads.
3+
Minimal, high-performance Python helpers for concurrent S3 object transfers.
44

5-
## Features
5+
A small extension that exposes fast, concurrent downloads (and uploads) to Python
6+
using a Rust implementation optimized for I/O-bound workloads.
67

7-
- **Fast downloads**: Concurrent downloads using async I/O
8-
- **Resilient**: Continues downloading even if some files fail
9-
- **Simple API**: Easy-to-use methods for single and batch downloads
10-
- **Detailed results**: Get success/failure information for batch operations
8+
## Install
119

12-
## Requirements
13-
14-
- Python >= 3.8
15-
16-
## Installation
10+
- From PyPI: `pip install robinzhon` or `uv add robinzhon`
11+
- From source (requires Rust + maturin):
1712

1813
```bash
19-
pip install robinzhon # if you use pip
20-
uv add robinzhon # if you use uv
14+
# build and install into active venv
15+
maturin develop --release
2116
```
2217

23-
## Example
18+
## Quick start
19+
20+
Download a single object:
2421

2522
```python
2623
from robinzhon import S3Downloader
2724

28-
# Initialize downloader
29-
client = S3Downloader("us-east-1")
30-
31-
# Download a single file
32-
download_path = client.download_file(
33-
"test-bucket", "test-object-key", "./test-object-key"
34-
)
35-
# download_path will be the file path where the object was downloaded
36-
37-
# Download multiple files to the same directory
38-
files_to_download = [
39-
"data/file1.csv",
40-
"data/file2.json",
41-
"logs/app.log"
42-
]
43-
result = client.download_multiple_files(
44-
"test-bucket", files_to_download, "./downloads"
45-
)
46-
47-
# Check results
48-
print(f"Downloaded {len(result.successful)} files successfully")
49-
print(f"Downloaded files: {result.successful}")
50-
if result.has_failures():
51-
print(f"Failed to download: {result.failed}")
52-
53-
# Download files with custom paths
54-
downloads = [
55-
("data/input.csv", "./processed/input_data.csv"),
56-
("config/settings.json", "./config/app_settings.json"),
57-
]
58-
result = client.download_multiple_files_with_paths("test-bucket", downloads)
59-
60-
print(f"Success rate: {result.success_rate():.1%}")
25+
d = S3Downloader("us-east-1")
26+
path = d.download_file("my-bucket", "path/to/object.txt", "./object.txt")
27+
print(path) # ./object.txt
28+
```
29+
30+
Download many objects into a directory:
31+
32+
```python
33+
files = ["data/a.csv", "data/b.csv"]
34+
res = d.download_multiple_files("my-bucket", files, "./downloads")
35+
print(res.successful)
36+
print(res.failed)
37+
```
38+
39+
Upload a single file or multiple files:
40+
41+
```python
42+
from robinzhon import S3Uploader
43+
44+
u = S3Uploader("us-east-1")
45+
u.upload_file("my-bucket", "dest/key.txt", "./local.txt")
46+
paths_and_keys = [("./local1.txt", "key1"), ("./local2.txt", "key2")]
47+
res = u.upload_multiple_files("my-bucket", paths_and_keys)
48+
print(res.successful, res.failed)
6149
```
6250

6351
## Configuration
6452

65-
You can configure the maximum number of concurrent downloads:
53+
- Both `S3Downloader` and `S3Uploader` accept an optional concurrency argument (default 5):
6654

6755
```python
68-
# Allow up to 10 concurrent downloads (default is 5)
69-
client = S3Downloader("us-east-1", max_concurrent_downloads=10)
56+
S3Downloader("us-east-1", max_concurrent_downloads=10)
57+
S3Uploader("us-east-1", max_concurrent_uploads=10)
7058
```
7159

72-
## Performance Test Results
60+
## API summary
7361

74-
```text
75-
============================================================
76-
Performance Test: 1000 files
77-
============================================================
62+
- Results
63+
- Attributes: `successful: List[str]`, `failed: List[str]`
64+
- Methods: `is_complete_success()`, `has_success()`, `has_failures()`, `total_count()`, `success_rate()`
7865

79-
Testing threaded boto3 implementation...
80-
Completed in 24.16s
66+
- S3Downloader(region_name, max_concurrent_downloads=5)
67+
- `download_file(bucket, key, local_path) -> str`
68+
- `download_multiple_files(bucket, keys, base_dir) -> Results`
69+
- `download_multiple_files_with_paths(bucket, [(key, local_path), ...]) -> Results`
8170

82-
Testing aioboto3 async implementation...
83-
Completed in 27.70s
71+
- S3Uploader(region_name, max_concurrent_uploads=5)
72+
- `upload_file(bucket, key, local_path) -> str`
73+
- `upload_multiple_files(bucket, [(local_path, key), ...]) -> Results`
8474

85-
Testing robinzhon implementation...
86-
Download completed in 10.30s
87-
Verifying robinzhon downloads...
75+
## Building & testing
8876

89-
Performance Results (1000 files)
90-
================================================================================
91-
Metric robinzhon threaded boto3 aioboto3 Winner
92-
================================================================================
93-
Duration (seconds) 10.30 24.16 27.70 robinzhon
94-
Throughput (files/sec) 97.1 41.4 36.1 robinzhon
95-
Success Rate (%) 100.0 100.0 100.0 robinzhon
96-
Files Downloaded 1000 1000 1000
97-
================================================================================
77+
- Requires Rust toolchain and `maturin` to build the extension.
78+
- Tests are simple Python tests that assume the compiled extension is available.
9879

99-
Performance Summary:
100-
robinzhon is 2.3x faster than threaded boto3
101-
robinzhon is 2.7x faster than aioboto3
102-
```
80+
## License
81+
82+
See the [LICENSE](LICENSE) file in the repository.

pyproject.toml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ keywords = [
99
"s3",
1010
"aws",
1111
"download",
12+
"s3-download",
13+
"upload",
14+
"s3-upload",
1215
"performance",
1316
"concurrent",
1417
"async",
1518
"rust",
16-
"fast",
17-
"boto3",
19+
"pyo3",
20+
"rust-extension",
1821
"cloud",
1922
"storage",
23+
"boto3",
2024
]
2125
classifiers = [
2226
"Development Status :: 4 - Beta",
@@ -25,20 +29,17 @@ classifiers = [
2529
"Operating System :: OS Independent",
2630
"Programming Language :: Python",
2731
"Programming Language :: Python :: 3",
28-
"Programming Language :: Python :: 3.8",
2932
"Programming Language :: Python :: 3.9",
3033
"Programming Language :: Python :: 3.10",
3134
"Programming Language :: Python :: 3.11",
3235
"Programming Language :: Python :: 3.12",
3336
"Programming Language :: Python :: 3.13",
3437
"Programming Language :: Rust",
3538
"Programming Language :: Python :: Implementation :: CPython",
36-
"Programming Language :: Python :: Implementation :: PyPy",
3739
"Topic :: Software Development :: Libraries :: Python Modules",
38-
"Topic :: Internet :: File Transfer Protocol (FTP)",
39-
"Topic :: System :: Archiving",
4040
"Topic :: Internet :: WWW/HTTP",
4141
"Topic :: Utilities",
42+
"Typing :: Typed",
4243
]
4344
dynamic = ["version"]
4445

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[pytest]
22
testpaths = tests
33
markers =
4-
performance: tests the performance of the implementation and compares against the original one
4+
performance: tests the performance of the implementation and compares against the original one

0 commit comments

Comments
 (0)