elaunira-r2index 0.1.0__tar.gz → 0.2.0__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.
Files changed (24) hide show
  1. elaunira_r2index-0.2.0/.gitignore +27 -0
  2. elaunira_r2index-0.2.0/PKG-INFO +164 -0
  3. elaunira_r2index-0.2.0/README.md +132 -0
  4. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/pyproject.toml +17 -12
  5. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/__init__.py +22 -49
  6. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/async_client.py +171 -14
  7. elaunira_r2index-0.1.0/src/elaunira/r2index/async_uploader.py → elaunira_r2index-0.2.0/src/elaunira/r2index/async_storage.py +69 -14
  8. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/client.py +169 -13
  9. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/exceptions.py +6 -0
  10. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/models.py +5 -0
  11. elaunira_r2index-0.2.0/src/elaunira/r2index/py.typed +0 -0
  12. elaunira_r2index-0.1.0/src/elaunira/r2index/uploader.py → elaunira_r2index-0.2.0/src/elaunira/r2index/storage.py +95 -15
  13. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/tests/test_async_client.py +3 -0
  14. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/tests/test_client.py +5 -0
  15. elaunira_r2index-0.2.0/tests/test_download.py +306 -0
  16. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/tests/test_models.py +6 -0
  17. elaunira_r2index-0.1.0/.gitignore +0 -4
  18. elaunira_r2index-0.1.0/PKG-INFO +0 -101
  19. elaunira_r2index-0.1.0/README.md +0 -69
  20. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/scripts/publish.sh +0 -0
  21. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/__init__.py +0 -0
  22. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/src/elaunira/r2index/checksums.py +0 -0
  23. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/tests/__init__.py +0 -0
  24. {elaunira_r2index-0.1.0 → elaunira_r2index-0.2.0}/tests/test_checksums.py +0 -0
@@ -0,0 +1,27 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+
21
+ # Testing
22
+ .pytest_cache/
23
+ .coverage
24
+ htmlcov/
25
+
26
+ # mypy
27
+ .mypy_cache/
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: elaunira-r2index
3
+ Version: 0.2.0
4
+ Summary: Python library for uploading files to R2 and registering them with the r2index API
5
+ Project-URL: Homepage, https://github.com/elaunira/elaunira-r2-index
6
+ Project-URL: Repository, https://github.com/elaunira/elaunira-r2-index
7
+ Author: Elaunira
8
+ License-Expression: MIT
9
+ Keywords: cloudflare,index,r2,storage,upload
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: aioboto3>=13.0.0
19
+ Requires-Dist: boto3>=1.35.0
20
+ Requires-Dist: httpx>=0.27.0
21
+ Requires-Dist: pydantic>=2.10.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: boto3-stubs[s3]>=1.35.0; extra == 'dev'
24
+ Requires-Dist: build>=1.2.0; extra == 'dev'
25
+ Requires-Dist: mypy>=1.15.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
27
+ Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
30
+ Requires-Dist: twine>=6.0.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # elaunira-r2index
34
+
35
+ Python library for uploading and downloading files to/from Cloudflare R2 with the r2index API.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install elaunira-r2index
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ### Sync Client
46
+
47
+ ```python
48
+ from elaunira.r2index import R2IndexClient, R2Config
49
+
50
+ client = R2IndexClient(
51
+ api_url="https://r2index.example.com",
52
+ api_token="your-bearer-token",
53
+ r2_config=R2Config(
54
+ access_key_id="your-r2-access-key-id",
55
+ secret_access_key="your-r2-secret-access-key",
56
+ endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
57
+ bucket="your-bucket-name",
58
+ ),
59
+ )
60
+
61
+ # Upload and register a file
62
+ record = client.upload_and_register(
63
+ local_path="./myfile.zip",
64
+ bucket="my-bucket",
65
+ category="software",
66
+ entity="myapp",
67
+ remote_path="/releases/myapp",
68
+ remote_filename="myapp.zip",
69
+ remote_version="v1",
70
+ tags=["release", "stable"],
71
+ )
72
+
73
+ # Download a file and record the download
74
+ # IP address is auto-detected, user agent defaults to "elaunira-r2index/0.1.0"
75
+ path, record = client.download_and_record(
76
+ bucket="my-bucket",
77
+ object_id="/releases/myapp/v1/myapp.zip",
78
+ destination="./downloads/myfile.zip",
79
+ )
80
+ ```
81
+
82
+ ### Async Client
83
+
84
+ ```python
85
+ from elaunira.r2index import AsyncR2IndexClient, R2Config
86
+
87
+ async with AsyncR2IndexClient(
88
+ api_url="https://r2index.example.com",
89
+ api_token="your-bearer-token",
90
+ r2_config=R2Config(
91
+ access_key_id="your-r2-access-key-id",
92
+ secret_access_key="your-r2-secret-access-key",
93
+ endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
94
+ bucket="your-bucket-name",
95
+ ),
96
+ ) as client:
97
+ # Upload
98
+ record = await client.upload_and_register(
99
+ local_path="./myfile.zip",
100
+ bucket="my-bucket",
101
+ category="software",
102
+ entity="myapp",
103
+ remote_path="/releases/myapp",
104
+ remote_filename="myapp.zip",
105
+ remote_version="v1",
106
+ tags=["release", "stable"],
107
+ )
108
+
109
+ # Download
110
+ path, record = await client.download_and_record(
111
+ bucket="my-bucket",
112
+ object_id="/releases/myapp/v1/myapp.zip",
113
+ destination="./downloads/myfile.zip",
114
+ )
115
+ ```
116
+
117
+ ### Transfer Configuration
118
+
119
+ Control multipart transfer settings with `R2TransferConfig`:
120
+
121
+ ```python
122
+ from elaunira.r2index import R2IndexClient, R2Config, R2TransferConfig
123
+
124
+ client = R2IndexClient(
125
+ api_url="https://r2index.example.com",
126
+ api_token="your-bearer-token",
127
+ r2_config=R2Config(...),
128
+ )
129
+
130
+ # Custom transfer settings
131
+ transfer_config = R2TransferConfig(
132
+ multipart_threshold=100 * 1024 * 1024, # 100MB (default)
133
+ multipart_chunksize=32 * 1024 * 1024, # 32MB chunks
134
+ max_concurrency=64, # 64 parallel threads
135
+ use_threads=True, # Enable threading (default)
136
+ )
137
+
138
+ path, record = client.download_and_record(
139
+ bucket="my-bucket",
140
+ object_id="/data/files/v2/largefile.zip",
141
+ destination="./downloads/largefile.zip",
142
+ transfer_config=transfer_config,
143
+ )
144
+ ```
145
+
146
+ Default `max_concurrency` is 2x the number of CPU cores (minimum 4).
147
+
148
+ ### Progress Tracking
149
+
150
+ ```python
151
+ def on_progress(bytes_transferred: int) -> None:
152
+ print(f"Downloaded: {bytes_transferred / 1024 / 1024:.1f} MB")
153
+
154
+ path, record = client.download_and_record(
155
+ bucket="my-bucket",
156
+ object_id="/releases/myapp/v1/myapp.zip",
157
+ destination="./downloads/myfile.zip",
158
+ progress_callback=on_progress,
159
+ )
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT
@@ -0,0 +1,132 @@
1
+ # elaunira-r2index
2
+
3
+ Python library for uploading and downloading files to/from Cloudflare R2 with the r2index API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install elaunira-r2index
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Sync Client
14
+
15
+ ```python
16
+ from elaunira.r2index import R2IndexClient, R2Config
17
+
18
+ client = R2IndexClient(
19
+ api_url="https://r2index.example.com",
20
+ api_token="your-bearer-token",
21
+ r2_config=R2Config(
22
+ access_key_id="your-r2-access-key-id",
23
+ secret_access_key="your-r2-secret-access-key",
24
+ endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
25
+ bucket="your-bucket-name",
26
+ ),
27
+ )
28
+
29
+ # Upload and register a file
30
+ record = client.upload_and_register(
31
+ local_path="./myfile.zip",
32
+ bucket="my-bucket",
33
+ category="software",
34
+ entity="myapp",
35
+ remote_path="/releases/myapp",
36
+ remote_filename="myapp.zip",
37
+ remote_version="v1",
38
+ tags=["release", "stable"],
39
+ )
40
+
41
+ # Download a file and record the download
42
+ # IP address is auto-detected, user agent defaults to "elaunira-r2index/0.1.0"
43
+ path, record = client.download_and_record(
44
+ bucket="my-bucket",
45
+ object_id="/releases/myapp/v1/myapp.zip",
46
+ destination="./downloads/myfile.zip",
47
+ )
48
+ ```
49
+
50
+ ### Async Client
51
+
52
+ ```python
53
+ from elaunira.r2index import AsyncR2IndexClient, R2Config
54
+
55
+ async with AsyncR2IndexClient(
56
+ api_url="https://r2index.example.com",
57
+ api_token="your-bearer-token",
58
+ r2_config=R2Config(
59
+ access_key_id="your-r2-access-key-id",
60
+ secret_access_key="your-r2-secret-access-key",
61
+ endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
62
+ bucket="your-bucket-name",
63
+ ),
64
+ ) as client:
65
+ # Upload
66
+ record = await client.upload_and_register(
67
+ local_path="./myfile.zip",
68
+ bucket="my-bucket",
69
+ category="software",
70
+ entity="myapp",
71
+ remote_path="/releases/myapp",
72
+ remote_filename="myapp.zip",
73
+ remote_version="v1",
74
+ tags=["release", "stable"],
75
+ )
76
+
77
+ # Download
78
+ path, record = await client.download_and_record(
79
+ bucket="my-bucket",
80
+ object_id="/releases/myapp/v1/myapp.zip",
81
+ destination="./downloads/myfile.zip",
82
+ )
83
+ ```
84
+
85
+ ### Transfer Configuration
86
+
87
+ Control multipart transfer settings with `R2TransferConfig`:
88
+
89
+ ```python
90
+ from elaunira.r2index import R2IndexClient, R2Config, R2TransferConfig
91
+
92
+ client = R2IndexClient(
93
+ api_url="https://r2index.example.com",
94
+ api_token="your-bearer-token",
95
+ r2_config=R2Config(...),
96
+ )
97
+
98
+ # Custom transfer settings
99
+ transfer_config = R2TransferConfig(
100
+ multipart_threshold=100 * 1024 * 1024, # 100MB (default)
101
+ multipart_chunksize=32 * 1024 * 1024, # 32MB chunks
102
+ max_concurrency=64, # 64 parallel threads
103
+ use_threads=True, # Enable threading (default)
104
+ )
105
+
106
+ path, record = client.download_and_record(
107
+ bucket="my-bucket",
108
+ object_id="/data/files/v2/largefile.zip",
109
+ destination="./downloads/largefile.zip",
110
+ transfer_config=transfer_config,
111
+ )
112
+ ```
113
+
114
+ Default `max_concurrency` is 2x the number of CPU cores (minimum 4).
115
+
116
+ ### Progress Tracking
117
+
118
+ ```python
119
+ def on_progress(bytes_transferred: int) -> None:
120
+ print(f"Downloaded: {bytes_transferred / 1024 / 1024:.1f} MB")
121
+
122
+ path, record = client.download_and_record(
123
+ bucket="my-bucket",
124
+ object_id="/releases/myapp/v1/myapp.zip",
125
+ destination="./downloads/myfile.zip",
126
+ progress_callback=on_progress,
127
+ )
128
+ ```
129
+
130
+ ## License
131
+
132
+ MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "elaunira-r2index"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Python library for uploading files to R2 and registering them with the r2index API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -23,22 +23,22 @@ classifiers = [
23
23
  "Typing :: Typed",
24
24
  ]
25
25
  dependencies = [
26
- "httpx>=0.25.0",
27
- "boto3>=1.34.0",
28
- "aioboto3>=12.0.0",
29
- "pydantic>=2.0.0",
26
+ "aioboto3>=13.0.0",
27
+ "boto3>=1.35.0",
28
+ "httpx>=0.27.0",
29
+ "pydantic>=2.10.0",
30
30
  ]
31
31
 
32
32
  [project.optional-dependencies]
33
33
  dev = [
34
+ "boto3-stubs[s3]>=1.35.0",
35
+ "build>=1.2.0",
36
+ "mypy>=1.15.0",
34
37
  "pytest>=8.0.0",
35
- "pytest-asyncio>=0.23.0",
36
- "pytest-httpx>=0.30.0",
37
- "ruff>=0.3.0",
38
- "mypy>=1.8.0",
39
- "boto3-stubs[s3]>=1.34.0",
40
- "build>=1.0.0",
41
- "twine>=5.0.0",
38
+ "pytest-asyncio>=0.25.0",
39
+ "pytest-httpx>=0.35.0",
40
+ "ruff>=0.9.0",
41
+ "twine>=6.0.0",
42
42
  ]
43
43
 
44
44
  [project.urls]
@@ -77,6 +77,11 @@ python_version = "3.12"
77
77
  strict = true
78
78
  warn_return_any = true
79
79
  warn_unused_ignores = true
80
+ plugins = ["pydantic.mypy"]
81
+
82
+ [[tool.mypy.overrides]]
83
+ module = ["aioboto3", "aioboto3.*", "aiobotocore", "aiobotocore.*"]
84
+ ignore_missing_imports = true
80
85
 
81
86
  [tool.pytest.ini_options]
82
87
  asyncio_mode = "auto"
@@ -1,43 +1,11 @@
1
- """
2
- R2 Index Python Library.
1
+ """Python library for uploading and downloading files to/from Cloudflare R2 with the r2index API."""
3
2
 
4
- A Python library for uploading files to Cloudflare R2 and registering them
5
- with the r2index API. Supports both sync and async operations with streaming
6
- checksums for memory-efficient handling of large files.
3
+ from importlib.metadata import version
7
4
 
8
- Usage:
9
- from elaunira.r2index import R2IndexClient, R2Config
10
-
11
- client = R2IndexClient(
12
- api_url="https://r2index.example.com",
13
- api_token="your-bearer-token",
14
- r2_config=R2Config(
15
- access_key_id="...",
16
- secret_access_key="...",
17
- endpoint_url="https://xxx.r2.cloudflarestorage.com",
18
- bucket="your-bucket",
19
- ),
20
- )
21
-
22
- # Upload and register a file
23
- record = client.upload_and_register(
24
- file_path="./myfile.zip",
25
- category="software",
26
- entity="myapp",
27
- remote_path="/releases",
28
- remote_filename="myapp-1.0.0.zip",
29
- remote_version="1.0.0",
30
- )
31
-
32
- Async usage:
33
- from elaunira.r2index import AsyncR2IndexClient, R2Config
34
-
35
- async with AsyncR2IndexClient(...) as client:
36
- record = await client.upload_and_register(...)
37
- """
5
+ __version__ = version("elaunira-r2index")
38
6
 
39
7
  from .async_client import AsyncR2IndexClient
40
- from .async_uploader import AsyncR2Uploader
8
+ from .async_storage import AsyncR2Storage
41
9
  from .checksums import (
42
10
  ChecksumResult,
43
11
  compute_checksums,
@@ -48,6 +16,7 @@ from .client import R2IndexClient
48
16
  from .exceptions import (
49
17
  AuthenticationError,
50
18
  ConflictError,
19
+ DownloadError,
51
20
  NotFoundError,
52
21
  R2IndexError,
53
22
  UploadError,
@@ -72,44 +41,48 @@ from .models import (
72
41
  UserAgentEntry,
73
42
  UserAgentsResponse,
74
43
  )
75
- from .uploader import R2Config, R2Uploader
44
+ from .storage import R2Config, R2Storage, R2TransferConfig
76
45
 
77
46
  __all__ = [
47
+ # Version
48
+ "__version__",
78
49
  # Clients
79
- "R2IndexClient",
80
50
  "AsyncR2IndexClient",
81
- # Uploaders
82
- "R2Uploader",
83
- "AsyncR2Uploader",
51
+ "R2IndexClient",
52
+ # Storage
53
+ "AsyncR2Storage",
84
54
  "R2Config",
55
+ "R2Storage",
56
+ "R2TransferConfig",
85
57
  # Checksums
86
58
  "ChecksumResult",
87
59
  "compute_checksums",
88
60
  "compute_checksums_async",
89
61
  "compute_checksums_from_file_object",
90
62
  # Exceptions
91
- "R2IndexError",
92
63
  "AuthenticationError",
93
- "NotFoundError",
94
- "ValidationError",
95
64
  "ConflictError",
65
+ "DownloadError",
66
+ "NotFoundError",
67
+ "R2IndexError",
96
68
  "UploadError",
69
+ "ValidationError",
97
70
  # Models - File operations
98
- "FileRecord",
99
71
  "FileCreateRequest",
100
- "FileUpdateRequest",
101
72
  "FileListResponse",
73
+ "FileRecord",
74
+ "FileUpdateRequest",
102
75
  "IndexEntry",
103
76
  "RemoteTuple",
104
77
  # Models - Downloads
105
78
  "DownloadRecord",
106
79
  "DownloadRecordRequest",
107
80
  # Models - Analytics
108
- "TimeseriesDataPoint",
109
- "TimeseriesResponse",
110
- "SummaryResponse",
111
81
  "DownloadByIpEntry",
112
82
  "DownloadsByIpResponse",
83
+ "SummaryResponse",
84
+ "TimeseriesDataPoint",
85
+ "TimeseriesResponse",
113
86
  "UserAgentEntry",
114
87
  "UserAgentsResponse",
115
88
  # Models - Other