django-fsspec 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.
- django_fsspec-0.1.0/.github/workflows/ci.yml +366 -0
- django_fsspec-0.1.0/.gitignore +9 -0
- django_fsspec-0.1.0/CHANGELOG.md +18 -0
- django_fsspec-0.1.0/LICENSE +21 -0
- django_fsspec-0.1.0/PKG-INFO +109 -0
- django_fsspec-0.1.0/README.md +94 -0
- django_fsspec-0.1.0/README_zh.md +94 -0
- django_fsspec-0.1.0/benchmarks/e2e_test.py +881 -0
- django_fsspec-0.1.0/benchmarks/run.py +467 -0
- django_fsspec-0.1.0/benchmarks/settings.py +65 -0
- django_fsspec-0.1.0/django_fsspec/__init__.py +1 -0
- django_fsspec-0.1.0/django_fsspec/_version.py +24 -0
- django_fsspec-0.1.0/django_fsspec/admin.py +11 -0
- django_fsspec-0.1.0/django_fsspec/apps.py +7 -0
- django_fsspec-0.1.0/django_fsspec/buffer.py +74 -0
- django_fsspec-0.1.0/django_fsspec/exceptions.py +14 -0
- django_fsspec-0.1.0/django_fsspec/fs.py +102 -0
- django_fsspec-0.1.0/django_fsspec/management/__init__.py +0 -0
- django_fsspec-0.1.0/django_fsspec/management/commands/__init__.py +0 -0
- django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_fsck.py +100 -0
- django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_gc.py +49 -0
- django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_stats.py +69 -0
- django_fsspec-0.1.0/django_fsspec/migrations/0001_initial.py +57 -0
- django_fsspec-0.1.0/django_fsspec/migrations/__init__.py +0 -0
- django_fsspec-0.1.0/django_fsspec/migrations_ops.py +96 -0
- django_fsspec-0.1.0/django_fsspec/models.py +58 -0
- django_fsspec-0.1.0/django_fsspec/operations.py +437 -0
- django_fsspec-0.1.0/django_fsspec/tests/__init__.py +0 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_admin.py +29 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_commands.py +194 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_fs.py +220 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_migrations_ops.py +118 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_models.py +103 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_operations.py +425 -0
- django_fsspec-0.1.0/django_fsspec/tests/test_validators.py +89 -0
- django_fsspec-0.1.0/django_fsspec/validators.py +52 -0
- django_fsspec-0.1.0/docker-compose.yml +30 -0
- django_fsspec-0.1.0/docs/en/architecture.md +99 -0
- django_fsspec-0.1.0/docs/en/configuration.md +27 -0
- django_fsspec-0.1.0/docs/en/exceptions.md +52 -0
- django_fsspec-0.1.0/docs/en/getting-started.md +68 -0
- django_fsspec-0.1.0/docs/en/management-commands.md +67 -0
- django_fsspec-0.1.0/docs/en/migration-guide.md +57 -0
- django_fsspec-0.1.0/docs/en/usage.md +69 -0
- django_fsspec-0.1.0/docs/zh/architecture.md +99 -0
- django_fsspec-0.1.0/docs/zh/configuration.md +27 -0
- django_fsspec-0.1.0/docs/zh/exceptions.md +52 -0
- django_fsspec-0.1.0/docs/zh/getting-started.md +69 -0
- django_fsspec-0.1.0/docs/zh/management-commands.md +69 -0
- django_fsspec-0.1.0/docs/zh/migration-guide.md +56 -0
- django_fsspec-0.1.0/docs/zh/usage.md +69 -0
- django_fsspec-0.1.0/pyproject.toml +47 -0
- django_fsspec-0.1.0/tests/__init__.py +0 -0
- django_fsspec-0.1.0/tests/settings.py +37 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
# ---------------------------------------------------------------
|
|
12
|
+
# Stage 1: Unit tests (SQLite, fast, matrix)
|
|
13
|
+
# ---------------------------------------------------------------
|
|
14
|
+
unit-test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
19
|
+
django-version: ["3.2", "4.2", "5.2"]
|
|
20
|
+
exclude:
|
|
21
|
+
- python-version: "3.12"
|
|
22
|
+
django-version: "3.2"
|
|
23
|
+
- python-version: "3.13"
|
|
24
|
+
django-version: "3.2"
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: |
|
|
36
|
+
pip install -e ".[dev]"
|
|
37
|
+
pip install "django~=${{ matrix.django-version }}.0"
|
|
38
|
+
|
|
39
|
+
- name: Run unit tests
|
|
40
|
+
run: |
|
|
41
|
+
python -m pytest django_fsspec/tests/ -v --cov=django_fsspec --cov-report=term-missing
|
|
42
|
+
|
|
43
|
+
lint:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- name: Set up Python
|
|
49
|
+
uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.11"
|
|
52
|
+
|
|
53
|
+
- name: Install dependencies
|
|
54
|
+
run: pip install -e ".[dev]"
|
|
55
|
+
|
|
56
|
+
- name: Check migrations
|
|
57
|
+
run: |
|
|
58
|
+
DJANGO_SETTINGS_MODULE=tests.settings python -m django makemigrations --check --dry-run
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------
|
|
61
|
+
# Stage 2: E2E + Benchmark (real databases, after unit tests pass)
|
|
62
|
+
# ---------------------------------------------------------------
|
|
63
|
+
e2e-sqlite:
|
|
64
|
+
needs: unit-test
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
|
|
69
|
+
- name: Set up Python
|
|
70
|
+
uses: actions/setup-python@v5
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.11"
|
|
73
|
+
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: pip install -e ".[dev]"
|
|
76
|
+
|
|
77
|
+
- name: E2E tests
|
|
78
|
+
run: DJANGO_FSSPEC_BENCH_DB=sqlite python benchmarks/e2e_test.py
|
|
79
|
+
|
|
80
|
+
- name: Benchmark
|
|
81
|
+
run: DJANGO_FSSPEC_BENCH_DB=sqlite python benchmarks/run.py --db sqlite --json benchmarks/results-sqlite.json
|
|
82
|
+
|
|
83
|
+
- name: Upload results
|
|
84
|
+
uses: actions/upload-artifact@v4
|
|
85
|
+
with:
|
|
86
|
+
name: benchmark-sqlite
|
|
87
|
+
path: benchmarks/results-sqlite.json
|
|
88
|
+
|
|
89
|
+
e2e-mysql:
|
|
90
|
+
needs: unit-test
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
strategy:
|
|
93
|
+
fail-fast: false
|
|
94
|
+
matrix:
|
|
95
|
+
include:
|
|
96
|
+
- mysql-version: "5.7"
|
|
97
|
+
django-version: "3.2"
|
|
98
|
+
- mysql-version: "8.0"
|
|
99
|
+
django-version: "5.2"
|
|
100
|
+
services:
|
|
101
|
+
mysql:
|
|
102
|
+
image: mysql:${{ matrix.mysql-version }}
|
|
103
|
+
env:
|
|
104
|
+
MYSQL_ROOT_PASSWORD: fsspec_test
|
|
105
|
+
MYSQL_DATABASE: fsspec_test
|
|
106
|
+
MYSQL_USER: fsspec
|
|
107
|
+
MYSQL_PASSWORD: fsspec_test
|
|
108
|
+
ports:
|
|
109
|
+
- 3306:3306
|
|
110
|
+
options: >-
|
|
111
|
+
--health-cmd="mysqladmin ping -h localhost -u root -pfsspec_test"
|
|
112
|
+
--health-interval=5s
|
|
113
|
+
--health-timeout=3s
|
|
114
|
+
--health-retries=10
|
|
115
|
+
|
|
116
|
+
steps:
|
|
117
|
+
- uses: actions/checkout@v4
|
|
118
|
+
|
|
119
|
+
- name: Set up Python
|
|
120
|
+
uses: actions/setup-python@v5
|
|
121
|
+
with:
|
|
122
|
+
python-version: "3.11"
|
|
123
|
+
|
|
124
|
+
- name: Configure MySQL charset
|
|
125
|
+
run: |
|
|
126
|
+
for i in 1 2 3 4 5; do
|
|
127
|
+
mysql -h 127.0.0.1 -P 3306 -u root -pfsspec_test -e "
|
|
128
|
+
ALTER DATABASE fsspec_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
129
|
+
" && break || sleep 3
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
- name: Install dependencies
|
|
133
|
+
run: |
|
|
134
|
+
pip install -e ".[dev]"
|
|
135
|
+
pip install mysqlclient
|
|
136
|
+
pip install "django~=${{ matrix.django-version }}.0"
|
|
137
|
+
|
|
138
|
+
- name: E2E tests
|
|
139
|
+
env:
|
|
140
|
+
DJANGO_FSSPEC_BENCH_DB: mysql
|
|
141
|
+
MYSQL_PORT: "3306"
|
|
142
|
+
run: python benchmarks/e2e_test.py
|
|
143
|
+
|
|
144
|
+
- name: Benchmark
|
|
145
|
+
env:
|
|
146
|
+
DJANGO_FSSPEC_BENCH_DB: mysql
|
|
147
|
+
MYSQL_PORT: "3306"
|
|
148
|
+
run: python benchmarks/run.py --db mysql --json benchmarks/results-mysql-${{ matrix.mysql-version }}.json
|
|
149
|
+
|
|
150
|
+
- name: Upload results
|
|
151
|
+
uses: actions/upload-artifact@v4
|
|
152
|
+
with:
|
|
153
|
+
name: benchmark-mysql-${{ matrix.mysql-version }}
|
|
154
|
+
path: benchmarks/results-mysql-${{ matrix.mysql-version }}.json
|
|
155
|
+
|
|
156
|
+
e2e-postgres:
|
|
157
|
+
needs: unit-test
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
strategy:
|
|
160
|
+
fail-fast: false
|
|
161
|
+
matrix:
|
|
162
|
+
include:
|
|
163
|
+
- pg-version: "9.6"
|
|
164
|
+
django-version: "3.2"
|
|
165
|
+
- pg-version: "16"
|
|
166
|
+
django-version: "5.2"
|
|
167
|
+
services:
|
|
168
|
+
postgres:
|
|
169
|
+
image: postgres:${{ matrix.pg-version }}
|
|
170
|
+
env:
|
|
171
|
+
POSTGRES_DB: fsspec_test
|
|
172
|
+
POSTGRES_USER: fsspec
|
|
173
|
+
POSTGRES_PASSWORD: fsspec_test
|
|
174
|
+
ports:
|
|
175
|
+
- 5432:5432
|
|
176
|
+
options: >-
|
|
177
|
+
--health-cmd="pg_isready -U fsspec -d fsspec_test"
|
|
178
|
+
--health-interval=5s
|
|
179
|
+
--health-timeout=3s
|
|
180
|
+
--health-retries=10
|
|
181
|
+
|
|
182
|
+
steps:
|
|
183
|
+
- uses: actions/checkout@v4
|
|
184
|
+
|
|
185
|
+
- name: Set up Python
|
|
186
|
+
uses: actions/setup-python@v5
|
|
187
|
+
with:
|
|
188
|
+
python-version: "3.11"
|
|
189
|
+
|
|
190
|
+
- name: Install dependencies
|
|
191
|
+
run: |
|
|
192
|
+
pip install -e ".[dev]"
|
|
193
|
+
pip install psycopg2-binary
|
|
194
|
+
pip install "django~=${{ matrix.django-version }}.0"
|
|
195
|
+
|
|
196
|
+
- name: E2E tests
|
|
197
|
+
env:
|
|
198
|
+
DJANGO_FSSPEC_BENCH_DB: postgres
|
|
199
|
+
POSTGRES_PORT: "5432"
|
|
200
|
+
run: python benchmarks/e2e_test.py
|
|
201
|
+
|
|
202
|
+
- name: Benchmark
|
|
203
|
+
env:
|
|
204
|
+
DJANGO_FSSPEC_BENCH_DB: postgres
|
|
205
|
+
POSTGRES_PORT: "5432"
|
|
206
|
+
run: python benchmarks/run.py --db postgres --json benchmarks/results-postgres-${{ matrix.pg-version }}.json
|
|
207
|
+
|
|
208
|
+
- name: Upload results
|
|
209
|
+
uses: actions/upload-artifact@v4
|
|
210
|
+
with:
|
|
211
|
+
name: benchmark-postgres-${{ matrix.pg-version }}
|
|
212
|
+
path: benchmarks/results-postgres-${{ matrix.pg-version }}.json
|
|
213
|
+
|
|
214
|
+
e2e-oracle:
|
|
215
|
+
needs: unit-test
|
|
216
|
+
runs-on: ubuntu-latest
|
|
217
|
+
services:
|
|
218
|
+
oracle:
|
|
219
|
+
image: gvenzl/oracle-free:23-slim
|
|
220
|
+
env:
|
|
221
|
+
ORACLE_PASSWORD: fsspec_test
|
|
222
|
+
APP_USER: fsspec
|
|
223
|
+
APP_USER_PASSWORD: fsspec_test
|
|
224
|
+
ports:
|
|
225
|
+
- 1521:1521
|
|
226
|
+
options: >-
|
|
227
|
+
--health-cmd="healthcheck.sh"
|
|
228
|
+
--health-interval=10s
|
|
229
|
+
--health-timeout=5s
|
|
230
|
+
--health-retries=30
|
|
231
|
+
--health-start-period=60s
|
|
232
|
+
|
|
233
|
+
steps:
|
|
234
|
+
- uses: actions/checkout@v4
|
|
235
|
+
|
|
236
|
+
- name: Set up Python
|
|
237
|
+
uses: actions/setup-python@v5
|
|
238
|
+
with:
|
|
239
|
+
python-version: "3.11"
|
|
240
|
+
|
|
241
|
+
- name: Install dependencies
|
|
242
|
+
run: |
|
|
243
|
+
pip install -e ".[dev]"
|
|
244
|
+
pip install "oracledb<4"
|
|
245
|
+
|
|
246
|
+
- name: E2E tests
|
|
247
|
+
env:
|
|
248
|
+
DJANGO_FSSPEC_BENCH_DB: oracle
|
|
249
|
+
run: python benchmarks/e2e_test.py
|
|
250
|
+
|
|
251
|
+
- name: Benchmark
|
|
252
|
+
env:
|
|
253
|
+
DJANGO_FSSPEC_BENCH_DB: oracle
|
|
254
|
+
run: python benchmarks/run.py --db oracle --json benchmarks/results-oracle.json
|
|
255
|
+
|
|
256
|
+
- name: Upload results
|
|
257
|
+
uses: actions/upload-artifact@v4
|
|
258
|
+
with:
|
|
259
|
+
name: benchmark-oracle
|
|
260
|
+
path: benchmarks/results-oracle.json
|
|
261
|
+
|
|
262
|
+
# ---------------------------------------------------------------
|
|
263
|
+
# Stage 3: Collect baseline
|
|
264
|
+
# ---------------------------------------------------------------
|
|
265
|
+
baseline:
|
|
266
|
+
needs: [e2e-sqlite, e2e-mysql, e2e-postgres, e2e-oracle]
|
|
267
|
+
if: github.ref == 'refs/heads/main'
|
|
268
|
+
runs-on: ubuntu-latest
|
|
269
|
+
steps:
|
|
270
|
+
- uses: actions/checkout@v4
|
|
271
|
+
|
|
272
|
+
- name: Download all benchmark results
|
|
273
|
+
uses: actions/download-artifact@v4
|
|
274
|
+
with:
|
|
275
|
+
path: benchmarks/results/
|
|
276
|
+
|
|
277
|
+
- name: Set up Python
|
|
278
|
+
uses: actions/setup-python@v5
|
|
279
|
+
with:
|
|
280
|
+
python-version: "3.11"
|
|
281
|
+
|
|
282
|
+
- name: Generate baseline report
|
|
283
|
+
run: |
|
|
284
|
+
python -c "
|
|
285
|
+
import json, glob
|
|
286
|
+
|
|
287
|
+
all_results = []
|
|
288
|
+
for f in sorted(glob.glob('benchmarks/results/**/*.json', recursive=True)):
|
|
289
|
+
with open(f) as fh:
|
|
290
|
+
all_results.extend(json.load(fh))
|
|
291
|
+
|
|
292
|
+
ops = {}
|
|
293
|
+
for r in all_results:
|
|
294
|
+
op = r.get('op', '?')
|
|
295
|
+
if op not in ops:
|
|
296
|
+
ops[op] = {}
|
|
297
|
+
ops[op][r.get('db', '?')] = r
|
|
298
|
+
|
|
299
|
+
print('## Performance Baseline')
|
|
300
|
+
print()
|
|
301
|
+
print('| Operation | Database | Avg (ms) | P95 (ms) | Ops/s |')
|
|
302
|
+
print('|-----------|----------|----------|----------|-------|')
|
|
303
|
+
for op, dbs in sorted(ops.items()):
|
|
304
|
+
for db, r in sorted(dbs.items()):
|
|
305
|
+
if 'error' in r:
|
|
306
|
+
print(f'| {op} | {db} | ERROR | | |')
|
|
307
|
+
else:
|
|
308
|
+
print(f'| {op} | {db} | {r[\"avg_ms\"]:.2f} | {r[\"p95_ms\"]:.2f} | {r[\"ops_per_sec\"]:.0f} |')
|
|
309
|
+
|
|
310
|
+
with open('benchmarks/baseline.json', 'w') as f:
|
|
311
|
+
json.dump(all_results, f, indent=2, default=str)
|
|
312
|
+
"
|
|
313
|
+
|
|
314
|
+
- name: Upload baseline
|
|
315
|
+
uses: actions/upload-artifact@v4
|
|
316
|
+
with:
|
|
317
|
+
name: performance-baseline
|
|
318
|
+
path: benchmarks/baseline.json
|
|
319
|
+
retention-days: 90
|
|
320
|
+
|
|
321
|
+
# ---------------------------------------------------------------
|
|
322
|
+
# Stage 4: Publish to PyPI (tag push only)
|
|
323
|
+
# ---------------------------------------------------------------
|
|
324
|
+
publish:
|
|
325
|
+
needs: [unit-test, lint, e2e-sqlite, e2e-mysql, e2e-postgres, e2e-oracle]
|
|
326
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
327
|
+
runs-on: ubuntu-latest
|
|
328
|
+
permissions:
|
|
329
|
+
id-token: write
|
|
330
|
+
steps:
|
|
331
|
+
- uses: actions/checkout@v4
|
|
332
|
+
with:
|
|
333
|
+
fetch-depth: 0
|
|
334
|
+
|
|
335
|
+
- name: Set up Python
|
|
336
|
+
uses: actions/setup-python@v5
|
|
337
|
+
with:
|
|
338
|
+
python-version: "3.11"
|
|
339
|
+
|
|
340
|
+
- name: Install build tools
|
|
341
|
+
run: pip install build twine
|
|
342
|
+
|
|
343
|
+
- name: Build package
|
|
344
|
+
run: python -m build
|
|
345
|
+
|
|
346
|
+
- name: Verify tag matches version
|
|
347
|
+
run: |
|
|
348
|
+
TAG=${GITHUB_REF#refs/tags/v}
|
|
349
|
+
VERSION=$(python -c "
|
|
350
|
+
import zipfile, glob
|
|
351
|
+
whl = glob.glob('dist/*.whl')[0]
|
|
352
|
+
# Extract version from wheel filename: name-version-...
|
|
353
|
+
version = whl.split('/')[-1].split('-')[1]
|
|
354
|
+
print(version)
|
|
355
|
+
")
|
|
356
|
+
echo "Tag: $TAG, Package version: $VERSION"
|
|
357
|
+
if [ "$TAG" != "$VERSION" ]; then
|
|
358
|
+
echo "ERROR: Tag v$TAG does not match package version $VERSION"
|
|
359
|
+
exit 1
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
- name: Publish to PyPI
|
|
363
|
+
env:
|
|
364
|
+
TWINE_USERNAME: __token__
|
|
365
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
366
|
+
run: twine upload dist/*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-06-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release
|
|
7
|
+
- FileNode, StorageBlock, FileBlock models with configurable block size
|
|
8
|
+
- fsspec integration via `DjangoFileSystem` (protocol: `django`)
|
|
9
|
+
- File modes: `rb`, `wb`, `ab`, `xb`
|
|
10
|
+
- Optimistic locking with version field for concurrent write detection
|
|
11
|
+
- Batch block allocation with free block pool reuse
|
|
12
|
+
- Path validation (blacklist + Unicode NFC normalization)
|
|
13
|
+
- Implicit directory support with database-side pushdown queries
|
|
14
|
+
- `RechunkOperation` for block size migration
|
|
15
|
+
- Management commands: `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
|
|
16
|
+
- Django Admin integration (FileNode read-only)
|
|
17
|
+
- Multi-database support (MySQL, PostgreSQL, Oracle, domestic databases)
|
|
18
|
+
- Namespace-based multi-tenancy
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MrLYC
|
|
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,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-fsspec
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Django app that provides a file system interface via fsspec, backed by Django ORM
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: django>=3.2
|
|
9
|
+
Requires-Dist: fsspec
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-django; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# django-fsspec
|
|
17
|
+
|
|
18
|
+
A Django app that provides a file system interface via [fsspec](https://filesystem-spec.readthedocs.io/), backed by Django ORM.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **fsspec compatible** — use standard `fsspec.filesystem("django")` API
|
|
23
|
+
- **Multi-database** — MySQL, PostgreSQL, Oracle, SQLite, and domestic databases
|
|
24
|
+
- **Configurable block size** — tune storage granularity per deployment
|
|
25
|
+
- **Optimistic locking** — safe concurrent writes with conflict detection
|
|
26
|
+
- **Block pool reuse** — efficient storage with free block recycling
|
|
27
|
+
- **Namespace isolation** — multi-tenant support via integer namespace
|
|
28
|
+
- **Path validation** — blacklist rules + Unicode NFC normalization
|
|
29
|
+
- **Implicit directories** — no directory records, derived from file paths
|
|
30
|
+
- **Management commands** — `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install django-fsspec
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Add to `INSTALLED_APPS`:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
INSTALLED_APPS = [
|
|
42
|
+
...
|
|
43
|
+
"django_fsspec",
|
|
44
|
+
]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Run migrations:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python manage.py migrate
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use it:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import fsspec
|
|
57
|
+
|
|
58
|
+
fs = fsspec.filesystem("django", namespace=0)
|
|
59
|
+
|
|
60
|
+
# Write
|
|
61
|
+
with fs.open("/hello.txt", "wb") as f:
|
|
62
|
+
f.write(b"Hello World")
|
|
63
|
+
|
|
64
|
+
# Read
|
|
65
|
+
data = fs.cat("/hello.txt") # b"Hello World"
|
|
66
|
+
|
|
67
|
+
# List
|
|
68
|
+
fs.ls("/") # ["/hello.txt"]
|
|
69
|
+
|
|
70
|
+
# Delete
|
|
71
|
+
fs.rm("/hello.txt")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
Add to your Django `settings.py`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Block size in bytes (default: 256KB)
|
|
80
|
+
DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
|
|
81
|
+
|
|
82
|
+
# Maximum file size in bytes (default: 2MB)
|
|
83
|
+
DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Supported File Modes
|
|
87
|
+
|
|
88
|
+
| Mode | Description |
|
|
89
|
+
|------|-------------|
|
|
90
|
+
| `rb` | Read (file must exist) |
|
|
91
|
+
| `wb` | Write (create or overwrite) |
|
|
92
|
+
| `ab` | Append (create or append) |
|
|
93
|
+
| `xb` | Exclusive create (file must not exist) |
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
- [Getting Started](docs/en/getting-started.md)
|
|
98
|
+
- [Configuration](docs/en/configuration.md)
|
|
99
|
+
- [Usage Guide](docs/en/usage.md)
|
|
100
|
+
- [Architecture](docs/en/architecture.md)
|
|
101
|
+
- [Management Commands](docs/en/management-commands.md)
|
|
102
|
+
- [Block Size Migration](docs/en/migration-guide.md)
|
|
103
|
+
- [Exceptions](docs/en/exceptions.md)
|
|
104
|
+
|
|
105
|
+
[中文文档](README_zh.md) | [Chinese Documentation](README_zh.md)
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# django-fsspec
|
|
2
|
+
|
|
3
|
+
A Django app that provides a file system interface via [fsspec](https://filesystem-spec.readthedocs.io/), backed by Django ORM.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **fsspec compatible** — use standard `fsspec.filesystem("django")` API
|
|
8
|
+
- **Multi-database** — MySQL, PostgreSQL, Oracle, SQLite, and domestic databases
|
|
9
|
+
- **Configurable block size** — tune storage granularity per deployment
|
|
10
|
+
- **Optimistic locking** — safe concurrent writes with conflict detection
|
|
11
|
+
- **Block pool reuse** — efficient storage with free block recycling
|
|
12
|
+
- **Namespace isolation** — multi-tenant support via integer namespace
|
|
13
|
+
- **Path validation** — blacklist rules + Unicode NFC normalization
|
|
14
|
+
- **Implicit directories** — no directory records, derived from file paths
|
|
15
|
+
- **Management commands** — `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install django-fsspec
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Add to `INSTALLED_APPS`:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
INSTALLED_APPS = [
|
|
27
|
+
...
|
|
28
|
+
"django_fsspec",
|
|
29
|
+
]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run migrations:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python manage.py migrate
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Use it:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import fsspec
|
|
42
|
+
|
|
43
|
+
fs = fsspec.filesystem("django", namespace=0)
|
|
44
|
+
|
|
45
|
+
# Write
|
|
46
|
+
with fs.open("/hello.txt", "wb") as f:
|
|
47
|
+
f.write(b"Hello World")
|
|
48
|
+
|
|
49
|
+
# Read
|
|
50
|
+
data = fs.cat("/hello.txt") # b"Hello World"
|
|
51
|
+
|
|
52
|
+
# List
|
|
53
|
+
fs.ls("/") # ["/hello.txt"]
|
|
54
|
+
|
|
55
|
+
# Delete
|
|
56
|
+
fs.rm("/hello.txt")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
Add to your Django `settings.py`:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# Block size in bytes (default: 256KB)
|
|
65
|
+
DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
|
|
66
|
+
|
|
67
|
+
# Maximum file size in bytes (default: 2MB)
|
|
68
|
+
DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Supported File Modes
|
|
72
|
+
|
|
73
|
+
| Mode | Description |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `rb` | Read (file must exist) |
|
|
76
|
+
| `wb` | Write (create or overwrite) |
|
|
77
|
+
| `ab` | Append (create or append) |
|
|
78
|
+
| `xb` | Exclusive create (file must not exist) |
|
|
79
|
+
|
|
80
|
+
## Documentation
|
|
81
|
+
|
|
82
|
+
- [Getting Started](docs/en/getting-started.md)
|
|
83
|
+
- [Configuration](docs/en/configuration.md)
|
|
84
|
+
- [Usage Guide](docs/en/usage.md)
|
|
85
|
+
- [Architecture](docs/en/architecture.md)
|
|
86
|
+
- [Management Commands](docs/en/management-commands.md)
|
|
87
|
+
- [Block Size Migration](docs/en/migration-guide.md)
|
|
88
|
+
- [Exceptions](docs/en/exceptions.md)
|
|
89
|
+
|
|
90
|
+
[中文文档](README_zh.md) | [Chinese Documentation](README_zh.md)
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# django-fsspec
|
|
2
|
+
|
|
3
|
+
基于 Django ORM 的文件系统,通过 [fsspec](https://filesystem-spec.readthedocs.io/) 提供标准接口。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **fsspec 兼容** — 使用标准 `fsspec.filesystem("django")` API
|
|
8
|
+
- **多数据库支持** — MySQL、PostgreSQL、Oracle、SQLite、信创数据库
|
|
9
|
+
- **可配置块大小** — 按部署需求调整存储粒度
|
|
10
|
+
- **乐观锁** — 安全的并发写入与冲突检测
|
|
11
|
+
- **块池复用** — 空闲块回收,高效存储
|
|
12
|
+
- **命名空间隔离** — 整数命名空间实现多租户
|
|
13
|
+
- **路径校验** — 黑名单规则 + Unicode NFC 归一化
|
|
14
|
+
- **隐式目录** — 无目录记录,从文件路径推导
|
|
15
|
+
- **管理命令** — `fsspec_gc`、`fsspec_fsck`、`fsspec_stats`
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install django-fsspec
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
添加到 `INSTALLED_APPS`:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
INSTALLED_APPS = [
|
|
27
|
+
...
|
|
28
|
+
"django_fsspec",
|
|
29
|
+
]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
运行迁移:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python manage.py migrate
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
使用:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import fsspec
|
|
42
|
+
|
|
43
|
+
fs = fsspec.filesystem("django", namespace=0)
|
|
44
|
+
|
|
45
|
+
# 写入
|
|
46
|
+
with fs.open("/hello.txt", "wb") as f:
|
|
47
|
+
f.write(b"Hello World")
|
|
48
|
+
|
|
49
|
+
# 读取
|
|
50
|
+
data = fs.cat("/hello.txt") # b"Hello World"
|
|
51
|
+
|
|
52
|
+
# 列目录
|
|
53
|
+
fs.ls("/") # ["/hello.txt"]
|
|
54
|
+
|
|
55
|
+
# 删除
|
|
56
|
+
fs.rm("/hello.txt")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 配置
|
|
60
|
+
|
|
61
|
+
在 Django `settings.py` 中添加:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# 块大小(字节),默认 256KB
|
|
65
|
+
DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
|
|
66
|
+
|
|
67
|
+
# 文件大小上限(字节),默认 2MB
|
|
68
|
+
DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 支持的文件模式
|
|
72
|
+
|
|
73
|
+
| 模式 | 说明 |
|
|
74
|
+
|------|------|
|
|
75
|
+
| `rb` | 只读(文件必须存在) |
|
|
76
|
+
| `wb` | 写入(创建或覆盖) |
|
|
77
|
+
| `ab` | 追加(创建或追加) |
|
|
78
|
+
| `xb` | 排他创建(文件必须不存在) |
|
|
79
|
+
|
|
80
|
+
## 文档
|
|
81
|
+
|
|
82
|
+
- [快速入门](docs/zh/getting-started.md)
|
|
83
|
+
- [配置说明](docs/zh/configuration.md)
|
|
84
|
+
- [使用指南](docs/zh/usage.md)
|
|
85
|
+
- [架构设计](docs/zh/architecture.md)
|
|
86
|
+
- [管理命令](docs/zh/management-commands.md)
|
|
87
|
+
- [块大小迁移](docs/zh/migration-guide.md)
|
|
88
|
+
- [异常体系](docs/zh/exceptions.md)
|
|
89
|
+
|
|
90
|
+
[English](README.md) | [英文文档](README.md)
|
|
91
|
+
|
|
92
|
+
## 许可证
|
|
93
|
+
|
|
94
|
+
MIT — 见 [LICENSE](LICENSE)。
|