furu 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl
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.
- furu/__init__.py +11 -1
- furu/adapters/submitit.py +23 -2
- furu/config.py +21 -3
- furu/core/__init__.py +2 -2
- furu/core/furu.py +708 -188
- furu/core/list.py +1 -1
- furu/dashboard/__init__.py +10 -1
- furu/dashboard/frontend/dist/assets/{index-CbdDfSOZ.css → index-BXAIKNNr.css} +1 -1
- furu/dashboard/frontend/dist/assets/{index-DDv_TYB_.js → index-DS3FsqcY.js} +3 -3
- furu/dashboard/frontend/dist/index.html +2 -2
- furu/dashboard/main.py +10 -3
- furu/errors.py +60 -5
- furu/execution/__init__.py +22 -0
- furu/execution/context.py +30 -0
- furu/execution/local.py +184 -0
- furu/execution/paths.py +20 -0
- furu/execution/plan.py +238 -0
- furu/execution/plan_utils.py +13 -0
- furu/execution/slurm_dag.py +271 -0
- furu/execution/slurm_pool.py +878 -0
- furu/execution/slurm_spec.py +38 -0
- furu/execution/submitit_factory.py +47 -0
- furu/migration.py +8 -4
- furu/runtime/logging.py +10 -10
- furu/serialization/serializer.py +40 -2
- furu/storage/metadata.py +17 -5
- furu/storage/state.py +78 -12
- {furu-0.0.2.dist-info → furu-0.0.4.dist-info}/METADATA +83 -33
- furu-0.0.4.dist-info/RECORD +46 -0
- furu-0.0.2.dist-info/RECORD +0 -36
- {furu-0.0.2.dist-info → furu-0.0.4.dist-info}/WHEEL +0 -0
- {furu-0.0.2.dist-info → furu-0.0.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: furu
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Cacheable, nested pipelines for Python. Define computations as configs; furu handles caching, state tracking, and result reuse across runs.
|
|
5
5
|
Author: Herman Brunborg
|
|
6
6
|
Author-email: Herman Brunborg <herman@brunborg.com>
|
|
@@ -44,7 +44,7 @@ The `[dashboard]` extra includes the web dashboard. Omit it for the core library
|
|
|
44
44
|
1. Subclass `furu.Furu[T]`
|
|
45
45
|
2. Implement `_create(self) -> T` (compute and write to `self.furu_dir`)
|
|
46
46
|
3. Implement `_load(self) -> T` (load from `self.furu_dir`)
|
|
47
|
-
4. Call `
|
|
47
|
+
4. Call `get()`
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
50
|
# my_project/pipelines.py
|
|
@@ -75,10 +75,10 @@ class TrainModel(furu.Furu[Path]):
|
|
|
75
75
|
from my_project.pipelines import TrainModel
|
|
76
76
|
|
|
77
77
|
# First call: runs _create(), caches result
|
|
78
|
-
artifact = TrainModel(lr=3e-4, steps=5000).
|
|
78
|
+
artifact = TrainModel(lr=3e-4, steps=5000).get()
|
|
79
79
|
|
|
80
80
|
# Second call with same config: loads from cache via _load()
|
|
81
|
-
artifact = TrainModel(lr=3e-4, steps=5000).
|
|
81
|
+
artifact = TrainModel(lr=3e-4, steps=5000).get()
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
> **Tip:** Define Furu classes in importable modules (not `__main__`); the artifact namespace is derived from the class's module + qualified name.
|
|
@@ -96,7 +96,7 @@ Each `Furu` instance maps deterministically to a directory based on its config:
|
|
|
96
96
|
- **namespace**: Derived from the class's module + qualified name (e.g., `my_project.pipelines/TrainModel`)
|
|
97
97
|
- **hash**: Computed from the object's config values using Blake2s
|
|
98
98
|
|
|
99
|
-
When you call `
|
|
99
|
+
When you call `get()`:
|
|
100
100
|
1. If no cached result exists → run `_create()`, save state as "success"
|
|
101
101
|
2. If cached result exists → run `_load()` to retrieve it
|
|
102
102
|
3. If another process is running → wait for it to finish, then load
|
|
@@ -123,7 +123,7 @@ class TrainTextModel(furu.Furu[str]):
|
|
|
123
123
|
dataset: Dataset = furu.chz.field(default_factory=Dataset)
|
|
124
124
|
|
|
125
125
|
def _create(self) -> str:
|
|
126
|
-
data = self.dataset.
|
|
126
|
+
data = self.dataset.get() # Triggers Dataset cache
|
|
127
127
|
(self.furu_dir / "model.txt").write_text(f"trained on:\n{data}")
|
|
128
128
|
return "trained"
|
|
129
129
|
|
|
@@ -131,6 +131,58 @@ class TrainTextModel(furu.Furu[str]):
|
|
|
131
131
|
return (self.furu_dir / "model.txt").read_text()
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Executors (Local + Slurm)
|
|
135
|
+
|
|
136
|
+
Use the execution helpers for batch runs and cluster scheduling:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from furu.execution import run_local
|
|
140
|
+
|
|
141
|
+
run_local(
|
|
142
|
+
[TrainModel(lr=3e-4, steps=5000), TrainModel(lr=1e-3, steps=2000)],
|
|
143
|
+
max_workers=8,
|
|
144
|
+
window_size="bfs",
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from furu.execution import SlurmSpec, submit_slurm_dag
|
|
150
|
+
|
|
151
|
+
specs = {
|
|
152
|
+
"default": SlurmSpec(partition="cpu", cpus=8, mem_gb=32, time_min=120),
|
|
153
|
+
"gpu": SlurmSpec(partition="gpu", gpus=1, cpus=8, mem_gb=64, time_min=720),
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
submit_slurm_dag([TrainModel(lr=3e-4, steps=5000)], specs=specs)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from furu.execution import run_slurm_pool
|
|
161
|
+
|
|
162
|
+
run_slurm_pool(
|
|
163
|
+
[TrainModel(lr=3e-4, steps=5000)],
|
|
164
|
+
specs=specs,
|
|
165
|
+
max_workers_total=50,
|
|
166
|
+
window_size="bfs",
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Submitit logs are stored under `<FURU_PATH>/submitit` by default. Override with
|
|
171
|
+
`FURU_SUBMITIT_PATH` when you want a different logs root.
|
|
172
|
+
|
|
173
|
+
### Breaking Changes and Executor Semantics
|
|
174
|
+
|
|
175
|
+
- `load_or_create()` is removed; use `get()` exclusively.
|
|
176
|
+
- `get()` no longer accepts per-call `retry_failed` overrides. Configure retries via
|
|
177
|
+
`FURU_RETRY_FAILED` or `FURU_CONFIG.retry_failed`.
|
|
178
|
+
- Executor runs (`run_local`, `run_slurm_pool`, `submit_slurm_dag`) fail fast if a
|
|
179
|
+
dependency is FAILED while `retry_failed` is disabled; with retries enabled, failed
|
|
180
|
+
compute nodes are retried (bounded by `FURU_MAX_COMPUTE_RETRIES` retries).
|
|
181
|
+
- Pool protocol/queue failures (invalid payloads, spec mismatch, missing artifacts) are
|
|
182
|
+
fatal even when `retry_failed` is enabled; only compute failures are retried.
|
|
183
|
+
- `FURU_ALWAYS_RERUN` causes matching nodes to recompute once per executor run, but
|
|
184
|
+
repeated references in the same run reuse that result.
|
|
185
|
+
|
|
134
186
|
### Storage Structure
|
|
135
187
|
|
|
136
188
|
Furu uses two roots: `FURU_PATH` for `data/` + `raw/`, and
|
|
@@ -176,7 +228,7 @@ class MyExperiments(furu.FuruList[TrainModel]):
|
|
|
176
228
|
|
|
177
229
|
# Iterate over all experiments
|
|
178
230
|
for exp in MyExperiments:
|
|
179
|
-
exp.
|
|
231
|
+
exp.get()
|
|
180
232
|
|
|
181
233
|
# Access by name
|
|
182
234
|
exp = MyExperiments.by_name("baseline")
|
|
@@ -191,14 +243,17 @@ for name, exp in MyExperiments.items():
|
|
|
191
243
|
|
|
192
244
|
### Custom Validation
|
|
193
245
|
|
|
194
|
-
Override `_validate()` to add custom cache invalidation logic
|
|
246
|
+
Override `_validate()` to add custom cache invalidation logic. Return False or
|
|
247
|
+
raise `furu.FuruValidationError` to force re-computation. In executor planning,
|
|
248
|
+
any other exception is logged and treated as invalid (no crash); in interactive
|
|
249
|
+
`exists()` calls, exceptions still surface:
|
|
195
250
|
|
|
196
251
|
```python
|
|
197
252
|
class ModelWithValidation(furu.Furu[Path]):
|
|
198
253
|
checkpoint_name: str = "model.pt"
|
|
199
254
|
|
|
200
255
|
def _validate(self) -> bool:
|
|
201
|
-
# Return False to force re-computation
|
|
256
|
+
# Return False (or raise FuruValidationError) to force re-computation
|
|
202
257
|
ckpt = self.furu_dir / self.checkpoint_name
|
|
203
258
|
return ckpt.exists() and ckpt.stat().st_size > 0
|
|
204
259
|
|
|
@@ -220,7 +275,7 @@ if obj.exists():
|
|
|
220
275
|
|
|
221
276
|
# Get metadata without triggering computation
|
|
222
277
|
metadata = obj.get_metadata()
|
|
223
|
-
print(f"Hash: {obj.
|
|
278
|
+
print(f"Hash: {obj.furu_hash}")
|
|
224
279
|
print(f"Dir: {obj.furu_dir}")
|
|
225
280
|
```
|
|
226
281
|
|
|
@@ -251,7 +306,7 @@ class LargeDataProcessor(furu.Furu[Path]):
|
|
|
251
306
|
def _create(self) -> Path:
|
|
252
307
|
# self.raw_dir is shared across all configs
|
|
253
308
|
# Create a subfolder for isolation if needed
|
|
254
|
-
my_raw = self.raw_dir / self.
|
|
309
|
+
my_raw = self.raw_dir / self.furu_hash
|
|
255
310
|
my_raw.mkdir(exist_ok=True)
|
|
256
311
|
|
|
257
312
|
large_file = my_raw / "huge_dataset.bin"
|
|
@@ -303,8 +358,8 @@ HHMMSS file.py:line message
|
|
|
303
358
|
|
|
304
359
|
Furu emits status messages like:
|
|
305
360
|
```
|
|
306
|
-
|
|
307
|
-
|
|
361
|
+
get TrainModel abc123def (missing->create)
|
|
362
|
+
get TrainModel abc123def (success->load)
|
|
308
363
|
```
|
|
309
364
|
|
|
310
365
|
### Explicit Setup
|
|
@@ -325,7 +380,7 @@ logger = furu.get_logger()
|
|
|
325
380
|
from furu import FuruComputeError, FuruWaitTimeout, FuruLockNotAcquired
|
|
326
381
|
|
|
327
382
|
try:
|
|
328
|
-
result = obj.
|
|
383
|
+
result = obj.get()
|
|
329
384
|
except FuruComputeError as e:
|
|
330
385
|
print(f"Computation failed: {e}")
|
|
331
386
|
print(f"State file: {e.state_path}")
|
|
@@ -336,29 +391,21 @@ except FuruLockNotAcquired:
|
|
|
336
391
|
print("Could not acquire lock")
|
|
337
392
|
```
|
|
338
393
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
Run computations on SLURM clusters via [submitit](https://github.com/facebookincubator/submitit):
|
|
394
|
+
By default, failed artifacts are retried on the next `get()` call. Set
|
|
395
|
+
`FURU_RETRY_FAILED=0` to keep failures sticky.
|
|
342
396
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
import furu
|
|
397
|
+
`FURU_MAX_WAIT_SECS` overrides the per-class `_max_wait_time_sec` (default 600s)
|
|
398
|
+
timeout used when waiting for compute locks before raising `FuruWaitTimeout`.
|
|
346
399
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
gpus_per_node=1,
|
|
352
|
-
)
|
|
400
|
+
Failures during metadata collection or signal handler setup (before `_create()`
|
|
401
|
+
runs) raise `FuruComputeError` with the original exception attached. These
|
|
402
|
+
failures still mark the attempt as failed and record details in `state.json`
|
|
403
|
+
and `furu.log`.
|
|
353
404
|
|
|
354
|
-
|
|
355
|
-
job = my_furu_obj.load_or_create(executor=executor)
|
|
356
|
-
|
|
357
|
-
# Job ID is tracked in .furu/state.json
|
|
358
|
-
print(job.job_id)
|
|
359
|
-
```
|
|
405
|
+
## Submitit Integration
|
|
360
406
|
|
|
361
|
-
Furu
|
|
407
|
+
Furu includes a `SubmititAdapter` for integrating submitit executors with the
|
|
408
|
+
state system. Executor helpers in `furu.execution` handle submission workflows.
|
|
362
409
|
|
|
363
410
|
## Dashboard
|
|
364
411
|
|
|
@@ -415,7 +462,10 @@ The `/api/experiments` endpoint supports:
|
|
|
415
462
|
| `FURU_LOG_LEVEL` | `INFO` | Console verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
|
|
416
463
|
| `FURU_IGNORE_DIFF` | `false` | Skip embedding git diff in metadata |
|
|
417
464
|
| `FURU_ALWAYS_RERUN` | `""` | Comma-separated class qualnames to always rerun (use `ALL` to bypass cache globally; cannot combine with other entries; entries must be importable) |
|
|
465
|
+
| `FURU_RETRY_FAILED` | `true` | Retry failed artifacts by default (set to `0` to keep failures sticky) |
|
|
466
|
+
| `FURU_MAX_COMPUTE_RETRIES` | `3` | Maximum compute retries per node after the first failure |
|
|
418
467
|
| `FURU_POLL_INTERVAL_SECS` | `10` | Polling interval for queued/running jobs |
|
|
468
|
+
| `FURU_MAX_WAIT_SECS` | unset | Override wait timeout (falls back to `_max_wait_time_sec`, default 600s) |
|
|
419
469
|
| `FURU_WAIT_LOG_EVERY_SECS` | `10` | Interval between "waiting" log messages |
|
|
420
470
|
| `FURU_STALE_AFTER_SECS` | `1800` | Consider running jobs stale after this duration |
|
|
421
471
|
| `FURU_LEASE_SECS` | `120` | Compute lock lease duration |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
furu/__init__.py,sha256=Z8VssTuQm2nH7bgB8SQc8pXsNGc-H1QGHFffKzNzqk8,2018
|
|
2
|
+
furu/adapters/__init__.py,sha256=onLzEj9hccPK15g8a8va2T19nqQXoxb9rQlJIjKSKnE,69
|
|
3
|
+
furu/adapters/submitit.py,sha256=FV3XEUSQuS5vIyzkW-Iuqtf8SRL-fsokPG67u7tMF5I,7276
|
|
4
|
+
furu/config.py,sha256=1nlJff4KNrWDvLhmnuLrsc7FJIxFLFhz3eOXZ8-ngX4,7349
|
|
5
|
+
furu/core/__init__.py,sha256=6hH7i6r627c0FZn6eQVsSG7LD4QmTta6iQw0AiPQPTM,156
|
|
6
|
+
furu/core/furu.py,sha256=Cy2cOnM5vsQoSk9nIVYj2Fx017wOQFPbxhnvYQsh7nI,58881
|
|
7
|
+
furu/core/list.py,sha256=xSuBT35p1anJ2fKQPxb-3cRTONUamFjfzkreVaI9Jo4,3614
|
|
8
|
+
furu/dashboard/__init__.py,sha256=ziAordJfkbbXNIM7iA9O7vR2gsCq34AInYiMYOCfWOc,362
|
|
9
|
+
furu/dashboard/__main__.py,sha256=cNs65IMl4kwZFpxa9xLXmFSy4-M5D1X1ZBfTDxW11vo,144
|
|
10
|
+
furu/dashboard/api/__init__.py,sha256=9-WyWOt-VQJJBIsdW29D-7JvR-BivJd9G_SRaRptCz0,80
|
|
11
|
+
furu/dashboard/api/models.py,sha256=SCu-kLJyW7dwSKswdgQNS3wQuj25ORs0pHkvX9xBbo4,4767
|
|
12
|
+
furu/dashboard/api/routes.py,sha256=iZez0khIUvbgfeSoy1BJvmoEEbgUrdSQA8SN8iAIkM8,4813
|
|
13
|
+
furu/dashboard/frontend/dist/assets/index-BXAIKNNr.css,sha256=qhsN0Td3mM-GAR8mZ0CtocynABLKa1ncl9ioDrTKOIQ,34768
|
|
14
|
+
furu/dashboard/frontend/dist/assets/index-DS3FsqcY.js,sha256=nfrKjhWThPtL8n5iTd9_1W-bsyMGwg2O8Iq2jkjj9Lg,544699
|
|
15
|
+
furu/dashboard/frontend/dist/favicon.svg,sha256=3TSLHNZITFe3JTPoYHZnDgiGsJxIzf39v97l2A1Hodo,369
|
|
16
|
+
furu/dashboard/frontend/dist/index.html,sha256=d9a8ZFKZ5uDtN3urqVNmS8LWMBhOC0eW7X0noT0RcYQ,810
|
|
17
|
+
furu/dashboard/main.py,sha256=gj9Cdj2qyaSCEkmfNHUMQXlXv6GpWTQ9IZEi7WzlCSo,4463
|
|
18
|
+
furu/dashboard/scanner.py,sha256=qXCvkvFByBc09TUdth5Js67rS8zpRBlRkVQ9dJ7YbdE,34696
|
|
19
|
+
furu/errors.py,sha256=FFbV4M0-ipVGizv5ee80L-NZFVjaRjy8i19mClr6R0g,3959
|
|
20
|
+
furu/execution/__init__.py,sha256=ixVw1Shvg2ulS597OYYeGgSSTwv25j_McuQdDXIiEL8,625
|
|
21
|
+
furu/execution/context.py,sha256=0tAbM0azqEus8hknf_A9-Zs9Sq99bnUkFyV4RO4ZMRU,666
|
|
22
|
+
furu/execution/local.py,sha256=TkKrRdmaQrN7i7Sxe87eHibRJOnz5OxU0Oj8qL_xP4I,7059
|
|
23
|
+
furu/execution/paths.py,sha256=0MfQk5Kh7bxvJiWvG40TJe7RF5Q5Na6uvi6qV0OT3Vc,460
|
|
24
|
+
furu/execution/plan.py,sha256=fM7CkXm_M0lL3vqdiNnWzbvMJAoSYKDBAnC82Af_rYM,6860
|
|
25
|
+
furu/execution/plan_utils.py,sha256=TAQqlPeJfOdH2MT-X7g3j1Se_0e4oKvG0tJaWC1kM40,381
|
|
26
|
+
furu/execution/slurm_dag.py,sha256=FOJcPKmIzRyrbJIq7heqGjKN0EFRMyOcV-yP7Ci87Qs,9360
|
|
27
|
+
furu/execution/slurm_pool.py,sha256=bi90fzZXAnoWHSPQba8Z3tk4_QMaqikWxCCzRfvDMvk,30400
|
|
28
|
+
furu/execution/slurm_spec.py,sha256=A1VX5K6aG8Ricg4fhnkz3Alkw_fx1bx53D0p4Ms3FqA,979
|
|
29
|
+
furu/execution/submitit_factory.py,sha256=B2vkDtmscuAX0sBaj9V5pNlgOtkkV35yJ1fZ7A-DSvU,1119
|
|
30
|
+
furu/migrate.py,sha256=x_Uh7oXAv40L5ZAHJhdnw-o7ct56rWUSZLbHHfRObeY,1313
|
|
31
|
+
furu/migration.py,sha256=R2-tARMx4VKryiqJ7WHia_dPVxRbTqofPpCFVE9zQ8U,31411
|
|
32
|
+
furu/runtime/__init__.py,sha256=fQqE7wUuWunLD73Vm3lss7BFSij3UVxXOKQXBAOS8zw,504
|
|
33
|
+
furu/runtime/env.py,sha256=o1phhoTDhOnhALr3Ozf1ldrdvk2ClyEvBWbebHM6BXg,160
|
|
34
|
+
furu/runtime/logging.py,sha256=WS3mB8VqMYUxPPI0yv1K-LnzVBj84Mnu1Qf9P2hCUUE,9652
|
|
35
|
+
furu/runtime/tracebacks.py,sha256=PGCuOq8QkWSoun791gjUXM8frOP2wWV8IBlqaA4nuGE,1631
|
|
36
|
+
furu/serialization/__init__.py,sha256=L7oHuIbxdSh7GCY3thMQnDwlt_ERH-TMy0YKEAZLrPs,341
|
|
37
|
+
furu/serialization/migrations.py,sha256=HD5g8JCBdH3Y0rHJYc4Ug1IXBVcUDxLE7nfiXZnXcUE,7772
|
|
38
|
+
furu/serialization/serializer.py,sha256=_nfUaAOy_KHegvfXlpPh4rCuvkzalJva75OvDg5nXiI,10114
|
|
39
|
+
furu/storage/__init__.py,sha256=cLLL-GPpSu9C72Mdk5S6TGu3g-SnBfEuxzfpx5ZJPtw,616
|
|
40
|
+
furu/storage/metadata.py,sha256=MH6w5hs-2rwHD6G9erMPM5pE3hm0h5Pk_G3Z6eyyGB0,9899
|
|
41
|
+
furu/storage/migration.py,sha256=Ars9aYwvhXpIBDf6L9ojGjp_l656-RfdtEAFKN0sZZY,2640
|
|
42
|
+
furu/storage/state.py,sha256=SFonqragT2eMCZbBKIvcA4JVe78rVmDRvo4Ky2IcNgc,43632
|
|
43
|
+
furu-0.0.4.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
44
|
+
furu-0.0.4.dist-info/entry_points.txt,sha256=hZkjtFzNlb33Zk-aUfLMRj-XgVDxdT82-JXG9d4bu2E,60
|
|
45
|
+
furu-0.0.4.dist-info/METADATA,sha256=fdUBvn-vEnVim9V5hAamE1sFuaKzWdwWPI17VU2Vyfc,16162
|
|
46
|
+
furu-0.0.4.dist-info/RECORD,,
|
furu-0.0.2.dist-info/RECORD
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
furu/__init__.py,sha256=fhSViHOJ9W-64swuaBFdZOfq0ZMuSj6LSiX2ZfcjhD8,1736
|
|
2
|
-
furu/adapters/__init__.py,sha256=onLzEj9hccPK15g8a8va2T19nqQXoxb9rQlJIjKSKnE,69
|
|
3
|
-
furu/adapters/submitit.py,sha256=OuCP0pEkO1kI4WLcSUvMqXwVCCy-8uwUE7v1qvkLZnU,6214
|
|
4
|
-
furu/config.py,sha256=C9mYQLgP4ciPmONCpQUu2YVV8adscCkfLsiyjXZVcpQ,6636
|
|
5
|
-
furu/core/__init__.py,sha256=gzFMgaAYnffofQksR6E1NegiwBF99h0ysn_QeD5wIhw,82
|
|
6
|
-
furu/core/furu.py,sha256=7swlMfGXBB_jmGABgMSl28v_qiE8Ot4vuDSos42cweQ,39085
|
|
7
|
-
furu/core/list.py,sha256=hwwlvqaKB1grPBGKXc15scF1RCqDvWc0AoDbhKlN4W0,3625
|
|
8
|
-
furu/dashboard/__init__.py,sha256=zNVddterfpjQtcpihIl3TRJdgdjOHYR0uO0cOSaGABg,172
|
|
9
|
-
furu/dashboard/__main__.py,sha256=cNs65IMl4kwZFpxa9xLXmFSy4-M5D1X1ZBfTDxW11vo,144
|
|
10
|
-
furu/dashboard/api/__init__.py,sha256=9-WyWOt-VQJJBIsdW29D-7JvR-BivJd9G_SRaRptCz0,80
|
|
11
|
-
furu/dashboard/api/models.py,sha256=SCu-kLJyW7dwSKswdgQNS3wQuj25ORs0pHkvX9xBbo4,4767
|
|
12
|
-
furu/dashboard/api/routes.py,sha256=iZez0khIUvbgfeSoy1BJvmoEEbgUrdSQA8SN8iAIkM8,4813
|
|
13
|
-
furu/dashboard/frontend/dist/assets/index-CbdDfSOZ.css,sha256=k3kxCuCqyxKgIv4M9itoAImMU8NMzkzAdTNQ4v_4fMU,34612
|
|
14
|
-
furu/dashboard/frontend/dist/assets/index-DDv_TYB_.js,sha256=FH0uqY7P7vm3rikvDaJ504FZh0Z97nCkVcIglK-ElAY,543928
|
|
15
|
-
furu/dashboard/frontend/dist/favicon.svg,sha256=3TSLHNZITFe3JTPoYHZnDgiGsJxIzf39v97l2A1Hodo,369
|
|
16
|
-
furu/dashboard/frontend/dist/index.html,sha256=o3XhvegC9rBpUiWNfXdCHqf_tg2795nob1NI0nBpFS4,810
|
|
17
|
-
furu/dashboard/main.py,sha256=8JYc79gbJ9MjvIRdGDuAcR2Mme9kyY4ryZb11ZZ4uVA,4069
|
|
18
|
-
furu/dashboard/scanner.py,sha256=qXCvkvFByBc09TUdth5Js67rS8zpRBlRkVQ9dJ7YbdE,34696
|
|
19
|
-
furu/errors.py,sha256=d1Kp5O9cVoQwXmQeZC-35u7xldw_c3ryYXrbVfv-Lws,2001
|
|
20
|
-
furu/migrate.py,sha256=x_Uh7oXAv40L5ZAHJhdnw-o7ct56rWUSZLbHHfRObeY,1313
|
|
21
|
-
furu/migration.py,sha256=A91dng1XRn1N_xJrmBhh-OvU22GlseqOh6PmVhNZh3w,31307
|
|
22
|
-
furu/runtime/__init__.py,sha256=fQqE7wUuWunLD73Vm3lss7BFSij3UVxXOKQXBAOS8zw,504
|
|
23
|
-
furu/runtime/env.py,sha256=o1phhoTDhOnhALr3Ozf1ldrdvk2ClyEvBWbebHM6BXg,160
|
|
24
|
-
furu/runtime/logging.py,sha256=JkuTFtbv6dYk088P6_Bga46bnKSDt-ElAqmiY86hMys,9773
|
|
25
|
-
furu/runtime/tracebacks.py,sha256=PGCuOq8QkWSoun791gjUXM8frOP2wWV8IBlqaA4nuGE,1631
|
|
26
|
-
furu/serialization/__init__.py,sha256=L7oHuIbxdSh7GCY3thMQnDwlt_ERH-TMy0YKEAZLrPs,341
|
|
27
|
-
furu/serialization/migrations.py,sha256=HD5g8JCBdH3Y0rHJYc4Ug1IXBVcUDxLE7nfiXZnXcUE,7772
|
|
28
|
-
furu/serialization/serializer.py,sha256=THWqHzpSwXj3Nj3PZ3JhwlWJ8sgvVyGrwBEDB_EWuAE,8355
|
|
29
|
-
furu/storage/__init__.py,sha256=cLLL-GPpSu9C72Mdk5S6TGu3g-SnBfEuxzfpx5ZJPtw,616
|
|
30
|
-
furu/storage/metadata.py,sha256=u4F4V1dDZtsiniO5xDCy8YxJZxGnreriYnJ1fOvQ2Bg,9232
|
|
31
|
-
furu/storage/migration.py,sha256=Ars9aYwvhXpIBDf6L9ojGjp_l656-RfdtEAFKN0sZZY,2640
|
|
32
|
-
furu/storage/state.py,sha256=q8wWJnGMWzx56PfsRMAMRB62p5vVw-iZ5rnUPfw2-js,40878
|
|
33
|
-
furu-0.0.2.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
34
|
-
furu-0.0.2.dist-info/entry_points.txt,sha256=hZkjtFzNlb33Zk-aUfLMRj-XgVDxdT82-JXG9d4bu2E,60
|
|
35
|
-
furu-0.0.2.dist-info/METADATA,sha256=8Zvp5E5XHn11a8YedVhAOi9KAvH9LwKvxiW4Jn7-Hsg,13833
|
|
36
|
-
furu-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|