quantilica-cli 0.10.0__tar.gz → 0.10.1__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.
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/CHANGELOG.md +8 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/PKG-INFO +2 -2
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/pyproject.toml +2 -2
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/sdk.py +10 -3
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_sdk.py +62 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/.githooks/pre-commit +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/.githooks/pre-push +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/.github/workflows/publish.yml +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/.github/workflows/test.yml +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/.gitignore +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/LICENSE +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/README.md +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/__init__.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/cli.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/health.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/manifests.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/progress.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/sources.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/cli/ui.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/src/quantilica/py.typed +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/__init__.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_health.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_manifests.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_package_hygiene.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_sdk_onda_a2.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_sources.py +0 -0
- {quantilica_cli-0.10.0 → quantilica_cli-0.10.1}/tests/test_ui.py +0 -0
|
@@ -5,6 +5,14 @@ Todas as mudanças notáveis deste projeto serão documentadas neste arquivo.
|
|
|
5
5
|
O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/),
|
|
6
6
|
e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
|
7
7
|
|
|
8
|
+
## [0.10.1] - 2026-10-08
|
|
9
|
+
|
|
10
|
+
### Corrigido
|
|
11
|
+
|
|
12
|
+
- `sdk`: `check_entry` considera `local.exists() or sidecar.exists()`, preservando
|
|
13
|
+
o status de `manifest-fresh` em ambientes de ingestão efêmera sem forçar
|
|
14
|
+
re-download redundante quando o dado bruto já foi espelhado.
|
|
15
|
+
|
|
8
16
|
## [0.10.0] - 2026-10-07
|
|
9
17
|
|
|
10
18
|
### Adicionado
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: quantilica-cli
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Unified CLI for Quantilica open data fetchers
|
|
5
5
|
Author-email: "Komesu, D.K." <daniel@dkko.me>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Python: >=3.12
|
|
19
|
-
Requires-Dist: quantilica-core>=0.10.
|
|
19
|
+
Requires-Dist: quantilica-core>=0.10.1
|
|
20
20
|
Requires-Dist: rich>=13.0.0
|
|
21
21
|
Requires-Dist: typer>=0.15.0
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quantilica-cli"
|
|
3
|
-
version = "0.10.
|
|
3
|
+
version = "0.10.1"
|
|
4
4
|
description = "Unified CLI for Quantilica open data fetchers"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Komesu, D.K.", email = "daniel@dkko.me" }]
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.13",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"quantilica-core>=0.10.
|
|
23
|
+
"quantilica-core>=0.10.1",
|
|
24
24
|
"rich>=13.0.0",
|
|
25
25
|
"typer>=0.15.0",
|
|
26
26
|
]
|
|
@@ -28,6 +28,7 @@ from quantilica.core.http import (
|
|
|
28
28
|
resolve_verify_from_env,
|
|
29
29
|
)
|
|
30
30
|
from quantilica.core.logging import get_logger
|
|
31
|
+
from quantilica.core.manifests import manifest_sidecar_path
|
|
31
32
|
from quantilica.core.storage import (
|
|
32
33
|
StampedDataRepository,
|
|
33
34
|
build_stamped_filename,
|
|
@@ -166,7 +167,9 @@ class CheckPlanItem:
|
|
|
166
167
|
remote_etag: Remote ETag header, or None.
|
|
167
168
|
remote_last_modified: Remote Last-Modified header, or None.
|
|
168
169
|
remote_size: Remote Content-Length, or None.
|
|
169
|
-
local_exists: Whether the
|
|
170
|
+
local_exists: Whether the artifact already exists locally — this
|
|
171
|
+
covers either the raw file itself or its ephemeral ``.manifest.json``
|
|
172
|
+
sidecar marker (file deleted for retention, sidecar still fresh).
|
|
170
173
|
action: ``"download"`` or ``"skip-up-to-date"``.
|
|
171
174
|
reason: Machine-readable reason for the action.
|
|
172
175
|
entry: Original dataset entry (allows ``sync --from-plan`` round-trip).
|
|
@@ -983,9 +986,13 @@ class FetcherApp:
|
|
|
983
986
|
size = None
|
|
984
987
|
last_modified = _parse_head_date(head.headers.get("Last-Modified"))
|
|
985
988
|
local = self.path_builder(output_dir, entry, last_modified)
|
|
986
|
-
|
|
989
|
+
sidecar = manifest_sidecar_path(local)
|
|
990
|
+
exists = local.exists() or sidecar.exists()
|
|
987
991
|
if exists and not is_remote_more_recent(head, local):
|
|
988
|
-
|
|
992
|
+
if local.exists():
|
|
993
|
+
action, reason = "skip-up-to-date", "local-fresh"
|
|
994
|
+
else:
|
|
995
|
+
action, reason = "skip-up-to-date", "manifest-fresh"
|
|
989
996
|
else:
|
|
990
997
|
action = "download"
|
|
991
998
|
reason = "not-present" if not exists else "remote-newer"
|
|
@@ -435,3 +435,65 @@ def test_default_client_attempts_env_override(monkeypatch):
|
|
|
435
435
|
monkeypatch.delenv("QUANTILICA_CA_BUNDLE", raising=False)
|
|
436
436
|
monkeypatch.setenv("QUANTILICA_HTTP_ATTEMPTS", "9")
|
|
437
437
|
assert default_client().attempts == 9
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def test_check_entry_ephemeral_manifest_fresh(tmp_path: Path):
|
|
441
|
+
"""Raw file deleted but sidecar manifest survives -> manifest-fresh skip."""
|
|
442
|
+
from quantilica.core.manifests import (
|
|
443
|
+
DownloadManifest,
|
|
444
|
+
SourceMetadata,
|
|
445
|
+
write_manifest_sidecar,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
app = make_app(client=_mock_client())
|
|
449
|
+
entry = sample_list_datasets("exp")[0]
|
|
450
|
+
local = default_path_builder(tmp_path, entry, dt.date(2024, 1, 1))
|
|
451
|
+
local.parent.mkdir(parents=True, exist_ok=True)
|
|
452
|
+
write_manifest_sidecar(
|
|
453
|
+
local,
|
|
454
|
+
DownloadManifest(
|
|
455
|
+
source_id="src",
|
|
456
|
+
dataset_id="ds",
|
|
457
|
+
url=entry["url"],
|
|
458
|
+
fetched_at="2024-01-02T00:00:00+00:00",
|
|
459
|
+
sha256="0" * 64,
|
|
460
|
+
size_bytes=10,
|
|
461
|
+
source_meta=SourceMetadata(
|
|
462
|
+
etag='"abc123"',
|
|
463
|
+
last_modified="Mon, 01 Jan 2024 00:00:00 GMT",
|
|
464
|
+
),
|
|
465
|
+
),
|
|
466
|
+
)
|
|
467
|
+
item = app.check_entry(entry, tmp_path)
|
|
468
|
+
assert item.action == "skip-up-to-date"
|
|
469
|
+
assert item.reason == "manifest-fresh"
|
|
470
|
+
assert item.local_exists is True
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def test_check_entry_ephemeral_stale_downloads(tmp_path: Path):
|
|
474
|
+
"""Sidecar with stale metadata (remote ETag differs) -> download."""
|
|
475
|
+
from quantilica.core.manifests import (
|
|
476
|
+
DownloadManifest,
|
|
477
|
+
SourceMetadata,
|
|
478
|
+
write_manifest_sidecar,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
app = make_app(client=_mock_client())
|
|
482
|
+
entry = sample_list_datasets("exp")[0]
|
|
483
|
+
local = default_path_builder(tmp_path, entry, dt.date(2024, 1, 1))
|
|
484
|
+
local.parent.mkdir(parents=True, exist_ok=True)
|
|
485
|
+
write_manifest_sidecar(
|
|
486
|
+
local,
|
|
487
|
+
DownloadManifest(
|
|
488
|
+
source_id="src",
|
|
489
|
+
dataset_id="ds",
|
|
490
|
+
url=entry["url"],
|
|
491
|
+
fetched_at="2023-12-31T00:00:00+00:00",
|
|
492
|
+
sha256="0" * 64,
|
|
493
|
+
size_bytes=10,
|
|
494
|
+
source_meta=SourceMetadata(etag='"outdated"', last_modified=None),
|
|
495
|
+
),
|
|
496
|
+
)
|
|
497
|
+
item = app.check_entry(entry, tmp_path)
|
|
498
|
+
assert item.action == "download"
|
|
499
|
+
assert item.reason == "remote-newer"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|