elaunira-r2index 0.1.0__tar.gz → 0.3.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 (25) hide show
  1. elaunira_r2index-0.3.0/.gitignore +27 -0
  2. elaunira_r2index-0.3.0/PKG-INFO +160 -0
  3. elaunira_r2index-0.3.0/README.md +128 -0
  4. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/pyproject.toml +17 -12
  5. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/__init__.py +22 -49
  6. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/async_client.py +205 -33
  7. elaunira_r2index-0.1.0/src/elaunira/r2index/async_uploader.py → elaunira_r2index-0.3.0/src/elaunira/r2index/async_storage.py +80 -19
  8. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/client.py +203 -32
  9. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/exceptions.py +6 -0
  10. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/models.py +5 -0
  11. elaunira_r2index-0.3.0/src/elaunira/r2index/py.typed +0 -0
  12. elaunira_r2index-0.3.0/src/elaunira/r2index/storage.py +232 -0
  13. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/tests/test_async_client.py +11 -8
  14. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/tests/test_client.py +21 -16
  15. elaunira_r2index-0.3.0/tests/test_download.py +302 -0
  16. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.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/src/elaunira/r2index/uploader.py +0 -147
  21. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/scripts/publish.sh +0 -0
  22. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/__init__.py +0 -0
  23. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/src/elaunira/r2index/checksums.py +0 -0
  24. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.0}/tests/__init__.py +0 -0
  25. {elaunira_r2index-0.1.0 → elaunira_r2index-0.3.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,160 @@
1
+ Metadata-Version: 2.4
2
+ Name: elaunira-r2index
3
+ Version: 0.3.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
49
+
50
+ client = R2IndexClient(
51
+ index_api_url="https://r2index.example.com",
52
+ index_api_token="your-bearer-token",
53
+ r2_access_key_id="your-r2-access-key-id",
54
+ r2_secret_access_key="your-r2-secret-access-key",
55
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
56
+ )
57
+
58
+ # Upload and register a file
59
+ record = client.upload(
60
+ bucket="my-bucket",
61
+ local_path="./myfile.zip",
62
+ category="software",
63
+ entity="myapp",
64
+ remote_path="/releases/myapp",
65
+ remote_filename="myapp.zip",
66
+ remote_version="v1",
67
+ tags=["release", "stable"],
68
+ )
69
+
70
+ # Download a file and record the download
71
+ # IP address is auto-detected, user agent defaults to "elaunira-r2index/<version>"
72
+ path, record = client.download(
73
+ bucket="my-bucket",
74
+ object_id="/releases/myapp/v1/myapp.zip",
75
+ destination="./downloads/myfile.zip",
76
+ )
77
+ ```
78
+
79
+ ### Async Client
80
+
81
+ ```python
82
+ from elaunira.r2index import AsyncR2IndexClient
83
+
84
+ async with AsyncR2IndexClient(
85
+ index_api_url="https://r2index.example.com",
86
+ index_api_token="your-bearer-token",
87
+ r2_access_key_id="your-r2-access-key-id",
88
+ r2_secret_access_key="your-r2-secret-access-key",
89
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
90
+ ) as client:
91
+ # Upload
92
+ record = await client.upload(
93
+ bucket="my-bucket",
94
+ local_path="./myfile.zip",
95
+ category="software",
96
+ entity="myapp",
97
+ remote_path="/releases/myapp",
98
+ remote_filename="myapp.zip",
99
+ remote_version="v1",
100
+ tags=["release", "stable"],
101
+ )
102
+
103
+ # Download
104
+ path, record = await client.download(
105
+ bucket="my-bucket",
106
+ object_id="/releases/myapp/v1/myapp.zip",
107
+ destination="./downloads/myfile.zip",
108
+ )
109
+ ```
110
+
111
+ ### Transfer Configuration
112
+
113
+ Control multipart transfer settings with `R2TransferConfig`:
114
+
115
+ ```python
116
+ from elaunira.r2index import R2IndexClient, R2TransferConfig
117
+
118
+ client = R2IndexClient(
119
+ index_api_url="https://r2index.example.com",
120
+ index_api_token="your-bearer-token",
121
+ r2_access_key_id="your-r2-access-key-id",
122
+ r2_secret_access_key="your-r2-secret-access-key",
123
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
124
+ )
125
+
126
+ # Custom transfer settings
127
+ transfer_config = R2TransferConfig(
128
+ multipart_threshold=100 * 1024 * 1024, # 100MB (default)
129
+ multipart_chunksize=32 * 1024 * 1024, # 32MB chunks
130
+ max_concurrency=64, # 64 parallel threads
131
+ use_threads=True, # Enable threading (default)
132
+ )
133
+
134
+ path, record = client.download(
135
+ bucket="my-bucket",
136
+ object_id="/data/files/v2/largefile.zip",
137
+ destination="./downloads/largefile.zip",
138
+ transfer_config=transfer_config,
139
+ )
140
+ ```
141
+
142
+ Default `max_concurrency` is 2x the number of CPU cores (minimum 4).
143
+
144
+ ### Progress Tracking
145
+
146
+ ```python
147
+ def on_progress(bytes_transferred: int) -> None:
148
+ print(f"Downloaded: {bytes_transferred / 1024 / 1024:.1f} MB")
149
+
150
+ path, record = client.download(
151
+ bucket="my-bucket",
152
+ object_id="/releases/myapp/v1/myapp.zip",
153
+ destination="./downloads/myfile.zip",
154
+ progress_callback=on_progress,
155
+ )
156
+ ```
157
+
158
+ ## License
159
+
160
+ MIT
@@ -0,0 +1,128 @@
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
17
+
18
+ client = R2IndexClient(
19
+ index_api_url="https://r2index.example.com",
20
+ index_api_token="your-bearer-token",
21
+ r2_access_key_id="your-r2-access-key-id",
22
+ r2_secret_access_key="your-r2-secret-access-key",
23
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
24
+ )
25
+
26
+ # Upload and register a file
27
+ record = client.upload(
28
+ bucket="my-bucket",
29
+ local_path="./myfile.zip",
30
+ category="software",
31
+ entity="myapp",
32
+ remote_path="/releases/myapp",
33
+ remote_filename="myapp.zip",
34
+ remote_version="v1",
35
+ tags=["release", "stable"],
36
+ )
37
+
38
+ # Download a file and record the download
39
+ # IP address is auto-detected, user agent defaults to "elaunira-r2index/<version>"
40
+ path, record = client.download(
41
+ bucket="my-bucket",
42
+ object_id="/releases/myapp/v1/myapp.zip",
43
+ destination="./downloads/myfile.zip",
44
+ )
45
+ ```
46
+
47
+ ### Async Client
48
+
49
+ ```python
50
+ from elaunira.r2index import AsyncR2IndexClient
51
+
52
+ async with AsyncR2IndexClient(
53
+ index_api_url="https://r2index.example.com",
54
+ index_api_token="your-bearer-token",
55
+ r2_access_key_id="your-r2-access-key-id",
56
+ r2_secret_access_key="your-r2-secret-access-key",
57
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
58
+ ) as client:
59
+ # Upload
60
+ record = await client.upload(
61
+ bucket="my-bucket",
62
+ local_path="./myfile.zip",
63
+ category="software",
64
+ entity="myapp",
65
+ remote_path="/releases/myapp",
66
+ remote_filename="myapp.zip",
67
+ remote_version="v1",
68
+ tags=["release", "stable"],
69
+ )
70
+
71
+ # Download
72
+ path, record = await client.download(
73
+ bucket="my-bucket",
74
+ object_id="/releases/myapp/v1/myapp.zip",
75
+ destination="./downloads/myfile.zip",
76
+ )
77
+ ```
78
+
79
+ ### Transfer Configuration
80
+
81
+ Control multipart transfer settings with `R2TransferConfig`:
82
+
83
+ ```python
84
+ from elaunira.r2index import R2IndexClient, R2TransferConfig
85
+
86
+ client = R2IndexClient(
87
+ index_api_url="https://r2index.example.com",
88
+ index_api_token="your-bearer-token",
89
+ r2_access_key_id="your-r2-access-key-id",
90
+ r2_secret_access_key="your-r2-secret-access-key",
91
+ r2_endpoint_url="https://your-account-id.r2.cloudflarestorage.com",
92
+ )
93
+
94
+ # Custom transfer settings
95
+ transfer_config = R2TransferConfig(
96
+ multipart_threshold=100 * 1024 * 1024, # 100MB (default)
97
+ multipart_chunksize=32 * 1024 * 1024, # 32MB chunks
98
+ max_concurrency=64, # 64 parallel threads
99
+ use_threads=True, # Enable threading (default)
100
+ )
101
+
102
+ path, record = client.download(
103
+ bucket="my-bucket",
104
+ object_id="/data/files/v2/largefile.zip",
105
+ destination="./downloads/largefile.zip",
106
+ transfer_config=transfer_config,
107
+ )
108
+ ```
109
+
110
+ Default `max_concurrency` is 2x the number of CPU cores (minimum 4).
111
+
112
+ ### Progress Tracking
113
+
114
+ ```python
115
+ def on_progress(bytes_transferred: int) -> None:
116
+ print(f"Downloaded: {bytes_transferred / 1024 / 1024:.1f} MB")
117
+
118
+ path, record = client.download(
119
+ bucket="my-bucket",
120
+ object_id="/releases/myapp/v1/myapp.zip",
121
+ destination="./downloads/myfile.zip",
122
+ progress_callback=on_progress,
123
+ )
124
+ ```
125
+
126
+ ## License
127
+
128
+ 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.3.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