genro-storage 0.1.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.
- genro_storage-0.1.0/LICENSE +21 -0
- genro_storage-0.1.0/MANIFEST.in +7 -0
- genro_storage-0.1.0/PKG-INFO +380 -0
- genro_storage-0.1.0/README.md +327 -0
- genro_storage-0.1.0/genro_storage/__init__.py +58 -0
- genro_storage-0.1.0/genro_storage/backends/__init__.py +21 -0
- genro_storage-0.1.0/genro_storage/backends/base.py +534 -0
- genro_storage-0.1.0/genro_storage/backends/base64.py +395 -0
- genro_storage-0.1.0/genro_storage/backends/fsspec.py +647 -0
- genro_storage-0.1.0/genro_storage/backends/local.py +307 -0
- genro_storage-0.1.0/genro_storage/exceptions.py +82 -0
- genro_storage-0.1.0/genro_storage/manager.py +514 -0
- genro_storage-0.1.0/genro_storage/node.py +1319 -0
- genro_storage-0.1.0/genro_storage/py.typed +1 -0
- genro_storage-0.1.0/genro_storage.egg-info/PKG-INFO +380 -0
- genro_storage-0.1.0/genro_storage.egg-info/SOURCES.txt +29 -0
- genro_storage-0.1.0/genro_storage.egg-info/dependency_links.txt +1 -0
- genro_storage-0.1.0/genro_storage.egg-info/requires.txt +34 -0
- genro_storage-0.1.0/genro_storage.egg-info/top_level.txt +1 -0
- genro_storage-0.1.0/pyproject.toml +101 -0
- genro_storage-0.1.0/requirements-docs.txt +5 -0
- genro_storage-0.1.0/setup.cfg +12 -0
- genro_storage-0.1.0/tests/test_advanced_features.py +545 -0
- genro_storage-0.1.0/tests/test_base64.py +369 -0
- genro_storage-0.1.0/tests/test_call_and_mimetype.py +332 -0
- genro_storage-0.1.0/tests/test_copy_skip_strategies.py +416 -0
- genro_storage-0.1.0/tests/test_local_storage.py +484 -0
- genro_storage-0.1.0/tests/test_md5_and_equality.py +295 -0
- genro_storage-0.1.0/tests/test_s3_integration.py +272 -0
- genro_storage-0.1.0/tests/test_serve.py +331 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Genropy Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: genro-storage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified storage abstraction for Genropy framework
|
|
5
|
+
Author-email: Genropy Team <info@genropy.org>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/genropy/genro-storage
|
|
8
|
+
Project-URL: Documentation, https://genro-storage.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/genropy/genro-storage
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/genropy/genro-storage/issues
|
|
11
|
+
Keywords: storage,filesystem,s3,cloud,genropy
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: System :: Filesystems
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: fsspec>=2023.1.0
|
|
26
|
+
Requires-Dist: PyYAML>=6.0
|
|
27
|
+
Provides-Extra: s3
|
|
28
|
+
Requires-Dist: s3fs>=2023.1.0; extra == "s3"
|
|
29
|
+
Provides-Extra: gcs
|
|
30
|
+
Requires-Dist: gcsfs>=2023.1.0; extra == "gcs"
|
|
31
|
+
Provides-Extra: azure
|
|
32
|
+
Requires-Dist: adlfs>=2023.1.0; extra == "azure"
|
|
33
|
+
Provides-Extra: http
|
|
34
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "http"
|
|
35
|
+
Provides-Extra: all
|
|
36
|
+
Requires-Dist: s3fs>=2023.1.0; extra == "all"
|
|
37
|
+
Requires-Dist: gcsfs>=2023.1.0; extra == "all"
|
|
38
|
+
Requires-Dist: adlfs>=2023.1.0; extra == "all"
|
|
39
|
+
Requires-Dist: aiohttp>=3.8.0; extra == "all"
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.24; extra == "docs"
|
|
44
|
+
Requires-Dist: myst-parser>=2.0; extra == "docs"
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
48
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
49
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
50
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
51
|
+
Requires-Dist: boto3>=1.26.0; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# genro-storage
|
|
55
|
+
|
|
56
|
+
[](https://www.python.org/downloads/)
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
[](https://genro-storage.readthedocs.io/en/latest/?badge=latest)
|
|
59
|
+
[](https://github.com/genropy/genro-storage/actions)
|
|
60
|
+
[](https://github.com/psf/black)
|
|
61
|
+
|
|
62
|
+
**Universal storage abstraction for Python with pluggable backends**
|
|
63
|
+
|
|
64
|
+
A modern, elegant Python library that provides a unified interface for accessing files across local filesystems, cloud storage (S3, GCS, Azure), and remote protocols (HTTP). Built on top of **fsspec**, genro-storage adds an intuitive mount-point system and user-friendly API inspired by Unix filesystems.
|
|
65
|
+
|
|
66
|
+
## Status: Beta - Ready for Production Testing
|
|
67
|
+
|
|
68
|
+
**Current Version:** 0.1.0-beta
|
|
69
|
+
**Last Updated:** October 2025
|
|
70
|
+
|
|
71
|
+
✅ Core implementation complete
|
|
72
|
+
✅ All backends working (local, S3, GCS, Azure, HTTP, Memory, Base64)
|
|
73
|
+
✅ 195 tests passing on Python 3.9-3.12
|
|
74
|
+
✅ Full documentation on ReadTheDocs
|
|
75
|
+
✅ Battle-tested code from 6+ years of Genropy production use
|
|
76
|
+
⚠️ Not yet on PyPI - install from source
|
|
77
|
+
|
|
78
|
+
## Key Features
|
|
79
|
+
|
|
80
|
+
- **Powered by fsspec** - Leverage 20+ battle-tested storage backends
|
|
81
|
+
- **Mount point system** - Organize storage with logical names like `home:`, `uploads:`, `s3:`
|
|
82
|
+
- **Intuitive API** - Pathlib-inspired interface that feels natural and Pythonic
|
|
83
|
+
- **Intelligent copy strategies** - Skip files by existence, size, or hash for efficient incremental backups
|
|
84
|
+
- **Progress tracking** - Built-in callbacks for progress bars and logging during copy operations
|
|
85
|
+
- **Content-based comparison** - Compare files by MD5 hash across different backends
|
|
86
|
+
- **Efficient hashing** - Uses cloud metadata (S3 ETag) when available, avoiding downloads
|
|
87
|
+
- **External tool integration** - `call()` method for seamless integration with ffmpeg, imagemagick, pandoc, etc.
|
|
88
|
+
- **WSGI file serving** - `serve()` method for web frameworks (Flask, Django, Pyramid) with ETag caching
|
|
89
|
+
- **MIME type detection** - Automatic content-type detection from file extensions
|
|
90
|
+
- **Flexible configuration** - Load mounts from YAML, JSON, or code
|
|
91
|
+
- **Dynamic paths** - Support for callable paths that resolve at runtime (perfect for user-specific directories)
|
|
92
|
+
- **Cloud metadata** - Get/set custom metadata on S3, GCS, Azure files
|
|
93
|
+
- **URL generation** - Generate presigned URLs for S3, public URLs for sharing
|
|
94
|
+
- **Base64 utilities** - Encode files to data URIs, download from URLs
|
|
95
|
+
- **S3 versioning** - Access historical file versions (when S3 versioning enabled)
|
|
96
|
+
- **Test-friendly** - In-memory backend for fast, isolated testing
|
|
97
|
+
- **Base64 data URIs** - Embed data inline with automatic encoding (writable with mutable paths)
|
|
98
|
+
- **Production-ready backends** - Built on 6+ years of Genropy production experience
|
|
99
|
+
- **Lightweight core** - Optional backends installed only when needed
|
|
100
|
+
- **Cross-storage operations** - Copy/move files between different storage types seamlessly
|
|
101
|
+
|
|
102
|
+
## Why genro-storage vs raw fsspec?
|
|
103
|
+
|
|
104
|
+
While **fsspec** is powerful, genro-storage provides:
|
|
105
|
+
|
|
106
|
+
- **Mount point abstraction** - Work with logical names instead of full URIs
|
|
107
|
+
- **Simpler API** - Less verbose, more intuitive for common operations
|
|
108
|
+
- **Configuration management** - Load storage configs from files
|
|
109
|
+
- **Enhanced utilities** - Cross-storage copy, unified error handling
|
|
110
|
+
|
|
111
|
+
Think of it as **"requests" is to "urllib"** - a friendlier interface to an excellent foundation.
|
|
112
|
+
|
|
113
|
+
## Perfect For
|
|
114
|
+
|
|
115
|
+
- **Multi-cloud applications** that need storage abstraction
|
|
116
|
+
- **Data pipelines** processing files from various sources
|
|
117
|
+
- **Web applications** managing uploads across environments
|
|
118
|
+
- **CLI tools** that work with local and remote files
|
|
119
|
+
- **Testing scenarios** requiring storage mocking
|
|
120
|
+
|
|
121
|
+
## Quick Example
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from genro_storage import StorageManager
|
|
125
|
+
|
|
126
|
+
# Configure storage backends
|
|
127
|
+
storage = StorageManager()
|
|
128
|
+
storage.configure([
|
|
129
|
+
{'name': 'home', 'type': 'local', 'path': '/home/user'},
|
|
130
|
+
{'name': 'uploads', 'type': 's3', 'bucket': 'my-app-uploads'},
|
|
131
|
+
{'name': 'backups', 'type': 'gcs', 'bucket': 'my-backups'},
|
|
132
|
+
{'name': 'data', 'type': 'base64'} # Inline base64 data
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
# Work with files using a unified API
|
|
136
|
+
node = storage.node('uploads:users/123/avatar.jpg')
|
|
137
|
+
if node.exists:
|
|
138
|
+
# Copy from S3 to local
|
|
139
|
+
node.copy(storage.node('home:cache/avatar.jpg'))
|
|
140
|
+
|
|
141
|
+
# Read and process
|
|
142
|
+
data = node.read_bytes()
|
|
143
|
+
|
|
144
|
+
# Backup to GCS
|
|
145
|
+
node.copy(storage.node('backups:avatars/user_123.jpg'))
|
|
146
|
+
|
|
147
|
+
# Base64 backend: embed data directly in URIs (data URI style)
|
|
148
|
+
# Read inline data
|
|
149
|
+
import base64
|
|
150
|
+
text = "Configuration data"
|
|
151
|
+
b64_data = base64.b64encode(text.encode()).decode()
|
|
152
|
+
node = storage.node(f'data:{b64_data}')
|
|
153
|
+
print(node.read_text()) # "Configuration data"
|
|
154
|
+
|
|
155
|
+
# Or write to create base64 (path updates automatically)
|
|
156
|
+
node = storage.node('data:')
|
|
157
|
+
node.write_text("New content")
|
|
158
|
+
print(node.path) # "TmV3IGNvbnRlbnQ=" (base64 of "New content")
|
|
159
|
+
|
|
160
|
+
# Copy from S3 to base64 for inline use
|
|
161
|
+
s3_image = storage.node('uploads:photo.jpg')
|
|
162
|
+
b64_image = storage.node('data:')
|
|
163
|
+
s3_image.copy(b64_image)
|
|
164
|
+
data_uri = f"data:image/jpeg;base64,{b64_image.path}"
|
|
165
|
+
|
|
166
|
+
# Advanced features
|
|
167
|
+
# 1. Intelligent incremental backups (NEW!)
|
|
168
|
+
docs = storage.node('home:documents')
|
|
169
|
+
s3_backup = storage.node('uploads:backup/documents')
|
|
170
|
+
|
|
171
|
+
# Skip files that already exist (fastest)
|
|
172
|
+
docs.copy(s3_backup, skip='exists')
|
|
173
|
+
|
|
174
|
+
# Skip files with same size (fast, good accuracy)
|
|
175
|
+
docs.copy(s3_backup, skip='size')
|
|
176
|
+
|
|
177
|
+
# Skip files with same content (accurate, uses S3 ETag - fast!)
|
|
178
|
+
docs.copy(s3_backup, skip='hash')
|
|
179
|
+
|
|
180
|
+
# With progress tracking
|
|
181
|
+
from tqdm import tqdm
|
|
182
|
+
pbar = tqdm(desc="Backing up", unit="file")
|
|
183
|
+
docs.copy(s3_backup, skip='hash',
|
|
184
|
+
progress=lambda cur, tot: pbar.update(1))
|
|
185
|
+
pbar.close()
|
|
186
|
+
|
|
187
|
+
# 2. Work with external tools using call() (ffmpeg, imagemagick, etc.)
|
|
188
|
+
video = storage.node('uploads:video.mp4')
|
|
189
|
+
thumbnail = storage.node('uploads:thumb.jpg')
|
|
190
|
+
|
|
191
|
+
# Automatically handles cloud download/upload
|
|
192
|
+
video.call('ffmpeg', '-i', video, '-vf', 'thumbnail', '-frames:v', '1', thumbnail)
|
|
193
|
+
|
|
194
|
+
# Or use local_path() for more control
|
|
195
|
+
with video.local_path(mode='r') as local_path:
|
|
196
|
+
import subprocess
|
|
197
|
+
subprocess.run(['ffmpeg', '-i', local_path, 'output.mp4'])
|
|
198
|
+
|
|
199
|
+
# 3. Serve files via WSGI (Flask, Django, Pyramid)
|
|
200
|
+
from flask import Flask, request
|
|
201
|
+
app = Flask(__name__)
|
|
202
|
+
|
|
203
|
+
@app.route('/files/<path:filepath>')
|
|
204
|
+
def serve_file(filepath):
|
|
205
|
+
node = storage.node(f'uploads:{filepath}')
|
|
206
|
+
# ETag caching, streaming, MIME types - all automatic!
|
|
207
|
+
return node.serve(request.environ, lambda s, h: None, cache_max_age=3600)
|
|
208
|
+
|
|
209
|
+
# 4. Check MIME types
|
|
210
|
+
doc = storage.node('uploads:report.pdf')
|
|
211
|
+
print(doc.mimetype) # 'application/pdf'
|
|
212
|
+
|
|
213
|
+
# 5. Dynamic paths for multi-user apps
|
|
214
|
+
def get_user_storage():
|
|
215
|
+
user_id = get_current_user()
|
|
216
|
+
return f'/data/users/{user_id}'
|
|
217
|
+
|
|
218
|
+
storage.configure([
|
|
219
|
+
{'name': 'user', 'type': 'local', 'path': get_user_storage}
|
|
220
|
+
])
|
|
221
|
+
# Path resolves differently per user!
|
|
222
|
+
|
|
223
|
+
# 6. Cloud metadata
|
|
224
|
+
file = storage.node('uploads:document.pdf')
|
|
225
|
+
file.set_metadata({
|
|
226
|
+
'Author': 'John Doe',
|
|
227
|
+
'Department': 'Engineering'
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
# 7. Generate shareable URLs
|
|
231
|
+
url = file.url(expires_in=3600) # S3 presigned URL
|
|
232
|
+
|
|
233
|
+
# 8. Encode to data URI
|
|
234
|
+
img = storage.node('home:logo.png')
|
|
235
|
+
data_uri = img.to_base64() # data:image/png;base64,...
|
|
236
|
+
|
|
237
|
+
# 9. Download from internet
|
|
238
|
+
remote = storage.node('uploads:downloaded.pdf')
|
|
239
|
+
remote.fill_from_url('https://example.com/file.pdf')
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Installation
|
|
243
|
+
|
|
244
|
+
### From GitHub (Recommended)
|
|
245
|
+
|
|
246
|
+
Install directly from GitHub without cloning:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Base package
|
|
250
|
+
pip install git+https://github.com/genropy/genro-storage.git
|
|
251
|
+
|
|
252
|
+
# With S3 support
|
|
253
|
+
pip install "genro-storage[s3] @ git+https://github.com/genropy/genro-storage.git"
|
|
254
|
+
|
|
255
|
+
# With all backends
|
|
256
|
+
pip install "genro-storage[all] @ git+https://github.com/genropy/genro-storage.git"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### From Source (Development)
|
|
260
|
+
|
|
261
|
+
Clone and install in editable mode:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Clone repository
|
|
265
|
+
git clone https://github.com/genropy/genro-storage.git
|
|
266
|
+
cd genro-storage
|
|
267
|
+
|
|
268
|
+
# Install base package
|
|
269
|
+
pip install -e .
|
|
270
|
+
|
|
271
|
+
# Install with S3 support
|
|
272
|
+
pip install -e ".[s3]"
|
|
273
|
+
|
|
274
|
+
# Install with all backends
|
|
275
|
+
pip install -e ".[all]"
|
|
276
|
+
|
|
277
|
+
# Install for development
|
|
278
|
+
pip install -e ".[all,dev]"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Supported Backends
|
|
282
|
+
|
|
283
|
+
Install optional dependencies for specific backends:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
pip install genro-storage[s3] # Amazon S3
|
|
287
|
+
pip install genro-storage[gcs] # Google Cloud Storage
|
|
288
|
+
pip install genro-storage[azure] # Azure Blob Storage
|
|
289
|
+
pip install genro-storage[http] # HTTP/HTTPS
|
|
290
|
+
pip install genro-storage[all] # All backends
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Testing
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Unit tests (fast, no external dependencies)
|
|
297
|
+
pytest tests/test_local_storage.py -v
|
|
298
|
+
|
|
299
|
+
# Integration tests (requires Docker + MinIO)
|
|
300
|
+
docker-compose up -d
|
|
301
|
+
pytest tests/test_s3_integration.py -v
|
|
302
|
+
|
|
303
|
+
# All tests
|
|
304
|
+
pytest tests/ -v
|
|
305
|
+
|
|
306
|
+
# With coverage
|
|
307
|
+
pytest tests/ -v --cov=genro_storage
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
See [TESTING.md](TESTING.md) for detailed testing instructions with MinIO.
|
|
311
|
+
|
|
312
|
+
## Documentation
|
|
313
|
+
|
|
314
|
+
- **[Full Documentation](https://genro-storage.readthedocs.io/)** - Complete API reference and guides
|
|
315
|
+
- **[API Design](API_DESIGN.md)** - Detailed design specification
|
|
316
|
+
- **[Testing Guide](TESTING.md)** - How to run tests with MinIO
|
|
317
|
+
|
|
318
|
+
## Built With
|
|
319
|
+
|
|
320
|
+
- [fsspec](https://filesystem-spec.readthedocs.io/) - Pythonic filesystem abstraction
|
|
321
|
+
- Modern Python (3.9+) with full type hints
|
|
322
|
+
- Optional backends: s3fs, gcsfs, adlfs, aiohttp
|
|
323
|
+
|
|
324
|
+
## Origins
|
|
325
|
+
|
|
326
|
+
genro-storage is extracted and modernized from [Genropy](https://github.com/genropy/genropy), a Python web framework with 6+ years of production battle-testing. We're making this powerful storage abstraction available as a standalone library for the wider Python community.
|
|
327
|
+
|
|
328
|
+
## Development Status
|
|
329
|
+
|
|
330
|
+
**Phase:** Beta - Production Testing
|
|
331
|
+
|
|
332
|
+
- ✅ API Design Complete and Stable
|
|
333
|
+
- ✅ Core Implementation Complete
|
|
334
|
+
- ✅ FsspecBackend (all 7 storage types working: local, S3, GCS, Azure, HTTP, Memory, Base64)
|
|
335
|
+
- ✅ Comprehensive Test Suite (195 tests, 79% coverage)
|
|
336
|
+
- ✅ CI/CD with Python 3.9, 3.10, 3.11, 3.12
|
|
337
|
+
- ✅ MD5 hashing and content-based equality
|
|
338
|
+
- ✅ Base64 backend with writable mutable paths
|
|
339
|
+
- ✅ Intelligent copy skip strategies (exists, size, hash, custom)
|
|
340
|
+
- ✅ call() method for external tool integration (ffmpeg, imagemagick, etc.)
|
|
341
|
+
- ✅ serve() method for WSGI file serving (Flask, Django, Pyramid)
|
|
342
|
+
- ✅ mimetype property for automatic content-type detection
|
|
343
|
+
- ✅ local_path() context manager for external tools
|
|
344
|
+
- ✅ Callable path support for dynamic directories
|
|
345
|
+
- ✅ Cloud metadata get/set (S3, GCS, Azure)
|
|
346
|
+
- ✅ URL generation (presigned URLs, data URIs)
|
|
347
|
+
- ✅ S3 versioning support
|
|
348
|
+
- ✅ Full Documentation on ReadTheDocs
|
|
349
|
+
- ✅ MinIO Integration Testing
|
|
350
|
+
- 🎯 Ready for early adopters and production testing
|
|
351
|
+
- ⏳ First PyPI release (v0.1.0)
|
|
352
|
+
- ⏳ Extended GCS/Azure integration testing
|
|
353
|
+
|
|
354
|
+
**Roadmap:**
|
|
355
|
+
- v0.1.0 (Q4 2025) - First PyPI release (Beta)
|
|
356
|
+
- v0.2.0 (Q1 2026) - Async support, performance optimizations
|
|
357
|
+
- v1.0.0 (2026) - Production-ready, stable API guarantee
|
|
358
|
+
|
|
359
|
+
## Contributing
|
|
360
|
+
|
|
361
|
+
Contributions welcome! The library is in beta with a stable API.
|
|
362
|
+
|
|
363
|
+
**How to contribute:**
|
|
364
|
+
1. Review the [API Design Document](API_DESIGN.md)
|
|
365
|
+
2. Check existing [tests](tests/) to understand behavior
|
|
366
|
+
3. Open an issue to discuss major changes
|
|
367
|
+
4. Submit PRs with tests
|
|
368
|
+
|
|
369
|
+
**Testing contributions:**
|
|
370
|
+
- Add tests for GCS and Azure backends
|
|
371
|
+
- Improve test coverage (target: 90%+)
|
|
372
|
+
- Add integration tests for edge cases
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
MIT License - See [LICENSE](LICENSE) for details
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
**Made with ❤️ by the Genropy team**
|