Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/scripts/cleanup_staging_bucket.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from datetime import UTC, datetime, timedelta

import boto3
from datetime import datetime, timedelta, timezone

DELETION_THRESHOLD_DAYS = 7
# Load config from environment
Expand All @@ -25,7 +26,7 @@
pages = paginator.paginate(Bucket=STAGING_BUCKET)

objects_to_delete = []
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
threshold = now - timedelta(days=DELETION_THRESHOLD_DAYS)

for page in pages:
Expand All @@ -50,7 +51,7 @@
response = client.delete_objects(
Bucket=STAGING_BUCKET, Delete={"Objects": chunk, "Quiet": True}
)
if "Errors" in response and response["Errors"]:
if response.get("Errors"):
print(" ❌ ERROR during batch deletion:")
for error in response["Errors"]:
print(
Expand Down
6 changes: 3 additions & 3 deletions .github/scripts/publish_script.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
import json
import os
import subprocess

from typing import Any

import boto3
from botocore.exceptions import ClientError

Expand Down Expand Up @@ -194,7 +194,7 @@ def handle_publications(manifest_data: list[dict[str, Any]]) -> bool:
if entry.get("description") == "pending-merge":
entry["description"] = commit_details["subject"]

if "staging_key" in entry and entry["staging_key"]:
if entry.get("staging_key"):
staging_key = entry.pop("staging_key")
final_key = entry["r2_object_key"]
print(f"Publishing: {dataset['fileName']} v{entry['version']}")
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.8
rev: 0.11.32
hooks:
# Dependency management
- id: uv-lock
Expand All @@ -23,7 +23,7 @@ repos:

# Python Linting & Formatting with Ruff
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
rev: v0.16.0
hooks:
- id: ruff
name: ruff (linter)
Expand All @@ -33,7 +33,7 @@ repos:

# Type checking with MyPy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.20.2
rev: v2.3.0
hooks:
- id: mypy
args: ["--config-file", "pyproject.toml"]
Expand Down
2 changes: 1 addition & 1 deletion src/datamanager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
from . import manifest as manifest
from .__main__ import app as app # keeps `python -m datamanager` handy

__all__ = ["app", "core", "manifest", "__version__"]
__all__ = ["__version__", "app", "core", "manifest"]
__version__: str = _dist_version("datamanager")
31 changes: 14 additions & 17 deletions src/datamanager/__main__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
# datamanager/__main__.py
import re
import subprocess
from datetime import datetime, timezone
import tempfile
import re
from dateutil.parser import isoparse
from collections.abc import Callable
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

import questionary
import typer
from dateutil.parser import isoparse
from rich.console import Console
from rich.table import Table

from typing import Callable, Optional, Any

from datamanager.config import settings
from datamanager import core, manifest

from datamanager.config import settings

# Common options for all commands
COMMON_OPTIONS = dict(
Expand All @@ -31,13 +30,13 @@
def _ask_confirm(ctx: typer.Context, prompt: str, default: bool = False) -> bool:
if ctx.obj.get("no_prompt"):
return True
result: Optional[bool] = questionary.confirm(prompt, default=default).ask()
result: bool | None = questionary.confirm(prompt, default=default).ask()
return bool(result) # Cast to bool to avoid NoneType issues


def _rel(iso: str) -> str:
dt = isoparse(iso)
delta = datetime.now(timezone.utc) - dt
delta = datetime.now(UTC) - dt
hours = int(delta.total_seconds() // 3600)
return f"{hours} h ago"

Expand Down Expand Up @@ -138,7 +137,7 @@ def list_datasets(ctx: typer.Context) -> None:
console.print(table)


def _run_pull_logic(name: str, version: str, output: Optional[Path]) -> None:
def _run_pull_logic(name: str, version: str, output: Path | None) -> None:
"""The core logic for pulling and verifying a dataset."""
console.print(f"🔎 Locating version '{version}' for dataset '{name}'...")
version_entry = manifest.get_version_entry(name, version)
Expand Down Expand Up @@ -188,7 +187,7 @@ def pull(
"-v",
help="Version to pull (e.g., 'v1'). Defaults to latest.",
),
output: Optional[Path] = typer.Option(
output: Path | None = typer.Option(
None,
"--output",
"-o",
Expand Down Expand Up @@ -330,7 +329,7 @@ def _run_prepare_logic(ctx: typer.Context, name: str, file: Path) -> None:
console.print(f"Change detected! Preparing new version: {new_version}")

console.print("Downloading previous version to generate diff...")
diff_git_path: Optional[Path] = None
diff_git_path: Path | None = None
with tempfile.TemporaryDirectory() as tempdir:
old_path = Path(tempdir) / "prev.sqlite"
# Download from the PRODUCTION bucket
Expand All @@ -355,7 +354,7 @@ def _run_prepare_logic(ctx: typer.Context, name: str, file: Path) -> None:

new_entry = {
"version": new_version,
"timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
"timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
"sha256": new_hash,
"r2_object_key": final_r2_key,
"staging_key": staging_key,
Expand All @@ -376,9 +375,7 @@ def _run_prepare_logic(ctx: typer.Context, name: str, file: Path) -> None:
"history": [
{
"version": "v1",
"timestamp": datetime.now(timezone.utc)
.isoformat()
.replace("+00:00", "Z"),
"timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
"sha256": new_hash,
"r2_object_key": f"{Path(Path(name).stem)}/v1-{new_hash}.sqlite",
"staging_key": staging_key,
Expand Down Expand Up @@ -492,7 +489,7 @@ def _run_rollback_logic(ctx: typer.Context, name: str, to_version: str) -> None:

rollback_entry = {
"version": new_version,
"timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
"timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
"sha256": target_entry["sha256"],
"r2_object_key": target_entry["r2_object_key"],
"diffFromPrevious": None,
Expand Down
6 changes: 4 additions & 2 deletions src/datamanager/config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# src/datamanager/config.py
from __future__ import annotations

import warnings
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from dotenv import find_dotenv, dotenv_values
import warnings

from dotenv import dotenv_values, find_dotenv

_ENV_PATH = Path(find_dotenv()) if find_dotenv() else None
_ENV = dotenv_values(_ENV_PATH) if _ENV_PATH else {}
Expand Down
14 changes: 5 additions & 9 deletions src/datamanager/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,22 @@
import difflib
import hashlib
import io
import os
import shutil
import sqlite3
import subprocess
from pathlib import Path, PurePath
import os
import uuid

from botocore.exceptions import ClientError
from pathlib import Path, PurePath
from typing import Any, TypedDict

import boto3
from rich.progress import Progress
from botocore.exceptions import ClientError
from rich.console import Console

from rich.progress import Progress
from types_boto3_s3.client import S3Client

from typing import Any, TypedDict

from datamanager.config import settings


console = Console()


Expand Down
16 changes: 8 additions & 8 deletions src/datamanager/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@

import json
from pathlib import Path
from typing import Any, Optional
from typing import Any

from rich.console import Console

from datamanager.config import settings

__all__ = [
"read_manifest",
"write_manifest",
"get_dataset",
"add_history_entry",
"update_latest_history_entry",
"get_version_entry",
"add_new_dataset",
"get_dataset",
"get_version_entry",
"read_manifest",
"update_dataset",
"update_latest_history_entry",
"write_manifest",
]

# Initialize console for any feedback
Expand Down Expand Up @@ -73,7 +73,7 @@ def update_latest_version(name: str, new_version: str) -> None:
write_manifest(data)


def get_dataset(name: str) -> Optional[dict[str, Any]]:
def get_dataset(name: str) -> dict[str, Any] | None:
"""
Finds and returns a single dataset from the manifest by its logical name.

Expand Down Expand Up @@ -158,7 +158,7 @@ def update_latest_history_entry(name: str, final_entry: dict[str, Any]) -> None:

def get_version_entry(
dataset_name: str, version: str = "latest"
) -> Optional[dict[str, Any]]:
) -> dict[str, Any] | None:
"""
Finds the history entry for a specific version of a dataset.

Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import shutil
import sqlite3
import subprocess
from collections.abc import Generator
from pathlib import Path
from typing import Any, Generator
from typing import Any

import pytest

Expand Down
4 changes: 2 additions & 2 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock
from pytest_mock import MockerFixture
import pytest

import pytest
from botocore.exceptions import ClientError
from pytest_mock import MockerFixture

from datamanager import core
from datamanager.config import settings
Expand Down
2 changes: 1 addition & 1 deletion tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from typer.testing import CliRunner

from datamanager import __main__ as main_app
from datamanager.__main__ import app
from datamanager import manifest
from datamanager.__main__ import app
from datamanager.config import settings

runner = CliRunner()
Expand Down
3 changes: 2 additions & 1 deletion tests/test_manifest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# tests/test_manifest.py
import json
import os
from pathlib import Path
import json

import pytest

from datamanager import manifest
Expand Down
Loading