Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/rw-python-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
if: ${{inputs.changed-python-modules == 'true'}}
strategy:
matrix:
python_version: [py314, py313, py312, py311, py310]
python_version: [py315, py314, py313, py312, py311]
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .sonar.settings
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# (C) 2021 GoodData Corporation
sonar.sources=gooddata-sdk,gooddata-fdw,gooddata-pandas,gooddata-flight-server,gooddata-flexconnect
sonar.exclusions=gooddata-api-client/**/*
sonar.python.version=3.10, 3.11, 3.12, 3.13
sonar.python.version=3.11, 3.12, 3.13, 3.14, 3.15
2 changes: 1 addition & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# (C) 2022 GoodData Corporation
sonar.sources=gooddata-sdk,gooddata-fdw,gooddata-pandas,gooddata-flight-server,gooddata-flexconnect
sonar.exclusions=gooddata-api-client/**/*
sonar.python.version=3.10, 3.11, 3.12, 3.13
sonar.python.version=3.11, 3.12, 3.13, 3.14, 3.15
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ helpers, managed as one `uv` workspace on a single shared version number.

## Tech Stack

- **Python** — `>=3.10` is the published floor; develop on 3.14. Tests run py310–py314.
- **Python** — `>=3.11` is the published floor; develop on 3.14. Tests run py311–py315.
- **uv** workspace (`uv ~= 0.12`) — one lock file at the root covers every package.
- **ruff** — lint and format. Line length 120, Google docstring convention.
- **ty** — type checking. Not mypy; `make type-check` runs `uv run ty check`.
Expand Down Expand Up @@ -86,7 +86,7 @@ meaningless once an earlier one has failed.
TEST_ENVS=py314 make -C packages/<name> test
```

`py314` is the fastest useful signal. Drop `TEST_ENVS` to run the full py310–py314 matrix
`py314` is the fastest useful signal. Drop `TEST_ENVS` to run the full py311–py315 matrix
the way CI does — slow, and worth it only when the change could be version-sensitive.

### 4. If a shared package changed, validate a consumer
Expand Down
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Getting Started

1. Ensure you have at minimum Python 3.14 installed; Python 3.13, 3.12, 3.11 and 3.10 are optional for multi-environment tests
1. Ensure you have at minimum Python 3.14 installed; Python 3.15, 3.13, 3.12 and 3.11 are optional for multi-environment tests

This repo uses [tox](https://tox.readthedocs.io/en/latest/) and by default will try to run tests against all
supported versions. If you have only subset of supported python interpreters installed, see
Expand Down Expand Up @@ -172,7 +172,7 @@ Here are the options how to run the tests:
- or execute `tox` command with arguments of your choice
```bash
cd packages/gooddata-sdk
tox -e py310
tox -e py311
```
- run tests for all non-client projects using `make test` in project root directory

Expand All @@ -185,7 +185,7 @@ Tests triggered by `make` can be controlled via these environment variables:
- `TEST_ENVS` - define tox test environments (targets) as comma-separated list, by default all tox default targets are
executed
```bash
TEST_ENVS=py311,py310 make test
TEST_ENVS=py312,py311 make test
```
- `ADD_ARGS` - send additional arguments to pytest tool, useful for pin-pointing just part of tests
```bash
Expand Down Expand Up @@ -384,9 +384,9 @@ venv automatically. So when docker tox tests are executed after localhost tests
cd packages/gooddata-sdk
make test-ci
```
- run all tests containing `http_headers` in name for py311 and py310 for all projects
- run all tests containing `http_headers` in name for py312 and py311 for all projects
```bash
TEST_ENVS=py311,py310 ADD_ARGS="-k http_headers" make test-ci
TEST_ENVS=py312,py311 ADD_ARGS="-k http_headers" make test-ci
```
- run tests on localhost against microservices started with docker-compose
```bash
Expand Down
10 changes: 5 additions & 5 deletions ci_tests.mk
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# (C) 2021 GoodData Corporation
IN_TEST_ENVS = py314,py313,py312,py311,py310
IN_TEST_ENVS = py315,py314,py313,py312,py311
ifdef TEST_ENVS
IN_TEST_ENVS = $(TEST_ENVS)
endif
Expand Down Expand Up @@ -35,10 +35,6 @@ endif


# Targets to build docker file for each python version
.PHONY: test-ci-py310-build
test-ci-py310-build: Dockerfile
docker build --build-arg "PY_TAG=3.10.19-slim-bookworm" --build-arg "ENV_TAG=py310" -t python-sdk:py310 .

.PHONY: test-ci-py311-build
test-ci-py311-build: Dockerfile
docker build --build-arg "PY_TAG=3.11.14-slim-bookworm" --build-arg "ENV_TAG=py311" -t python-sdk:py311 .
Expand All @@ -55,6 +51,10 @@ test-ci-py313-build: Dockerfile
test-ci-py314-build: Dockerfile
docker build --build-arg "PY_TAG=3.14.3-slim-bookworm" --build-arg "ENV_TAG=py314" -t python-sdk:py314 .

.PHONY: test-ci-py315-build
test-ci-py315-build: Dockerfile
docker build --build-arg "PY_TAG=3.15.0rc3-slim-bookworm" --build-arg "ENV_TAG=py315" -t python-sdk:py315 .

# test-ci target triggers unit tests for each requested environment
.PHONY: test-ci
test-ci: $(TEST_CI_ENVS)
Expand Down
4 changes: 2 additions & 2 deletions packages/gooddata-dbt/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "MIT"
authors = [
{name = "GoodData", email = "support@gooddata.com"}
]
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"gooddata-sdk~=1.76.0",
"pyyaml>=6.0",
Expand All @@ -20,11 +20,11 @@ dependencies = [
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Database",
"Topic :: Scientific/Engineering",
"Topic :: Software Development",
Expand Down
2 changes: 1 addition & 1 deletion packages/gooddata-dbt/tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# (C) 2023 GoodData Corporation
[tox]
envlist = py3{10,11,12,13,14}
envlist = py3{11,12,13,14,15}

[testenv]
runner = uv-venv-lock-runner
Expand Down
5 changes: 3 additions & 2 deletions packages/gooddata-eval/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = [
{name = "GoodData", email = "support@gooddata.com"}
]
keywords = ["gooddata", "ai", "evaluation", "llm", "analytics", "cli"]
requires-python = ">=3.10"
requires-python = ">=3.11"
dependencies = [
"gooddata-sdk~=1.76.0",
"httpx>=0.27,<1.0",
Expand All @@ -21,10 +21,11 @@ dependencies = [
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Scientific/Engineering",
"Topic :: Software Development",
"Typing :: Typed",
Expand Down
4 changes: 2 additions & 2 deletions packages/gooddata-eval/src/gooddata_eval/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os
import sys
import threading
from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path
from typing import get_args

Expand Down Expand Up @@ -498,7 +498,7 @@ def _run(config: RunConfig) -> int:
_reject_power_gate_on_ungated_items(config, items)
_warn_if_local_dataset_cannot_link(config, agentic_items)
models = config.models or []
run_ts = datetime.now(timezone.utc).strftime("%Y-%m-%d-%H-%M")
run_ts = datetime.now(UTC).strftime("%Y-%m-%d-%H-%M")
n_models = len(models) if models else 1

controller = WorkspaceModelController(config.host, config.token, config.workspace_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from enum import StrEnum


class AnomalyDetectionGranularity(str, Enum):
class AnomalyDetectionGranularity(StrEnum):
"""Detection intervals an anomaly alert accepts.

Mirrors gen-ai's enum of the same name; `StrEnum` is unavailable on the 3.10 floor, so
the `str` mixin carries the comparison against the raw tool argument.
Mirrors gen-ai's enum of the same name; being a `StrEnum`, members compare equal to the
raw tool argument.
"""

HOUR = "HOUR"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import time
from collections.abc import Iterator
from contextlib import contextmanager
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from typing import Any

from gooddata_eval.core.agentic._trace_linker import link_cancel_event, linking_is_inline, warn_from_worker
Expand Down Expand Up @@ -230,7 +230,7 @@ def find_traces_per_conversation(
return dict.fromkeys(conversation_ids)

by_conv: dict[str, Any] = dict.fromkeys(conversation_ids)
window_end = window_end or datetime.now(timezone.utc)
window_end = window_end or datetime.now(UTC)
pad = timedelta(seconds=_WINDOW_PADDING_SEC)

# One budget for the whole item. Per conversation it would multiply by --runs, and the
Expand Down Expand Up @@ -282,7 +282,7 @@ def _span_window(trace: Any, window: tuple[datetime, datetime] | None) -> tuple[
return start, end
if window is not None:
return window
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
return now, now


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from collections.abc import Callable
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import Any, Protocol

from gooddata_eval.core._output import emit_line
Expand Down Expand Up @@ -93,7 +93,7 @@ def link_cancel_event() -> threading.Event | None:

def utc_now() -> datetime:
"""Now, in UTC. One spelling, so a pinned trace window cannot drift by timezone."""
return datetime.now(timezone.utc)
return datetime.now(UTC)


def open_trace_window(langfuse: Any) -> tuple[Any, datetime]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import re
import sys
import unicodedata
from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path

from gooddata_sdk.catalog.workspace.aac import declarative_visualization_to_aac
Expand Down Expand Up @@ -291,7 +291,7 @@ def fetch_snapshot(sdk, workspace_id: str) -> dict:
ldm = sdk.catalog_workspace_content.get_declarative_ldm(workspace_id).ldm.to_dict(camel_case=True)
return {
"workspace_id": workspace_id,
"fetched_at": datetime.now(timezone.utc).isoformat(),
"fetched_at": datetime.now(UTC).isoformat(),
"analytics": analytics,
"display_names": build_display_names(analytics, ldm),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import threading
import time
import uuid
from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import Any

import httpx
Expand Down Expand Up @@ -102,7 +102,7 @@ def create(
dataset_id = self._owner.dataset_id_for_item(dataset_item_id)
if dataset_id is None:
raise LookupError(f"dataset item {dataset_item_id!r} not found in Langfuse")
now = datetime.now(timezone.utc)
now = datetime.now(UTC)
span = build_experiment_root_span(
ExperimentRun(run_name, dataset_id, metadata, run_description or None),
ExperimentItem(dataset_item_id, input={"dataset_item_id": dataset_item_id}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from __future__ import annotations

from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
Expand All @@ -23,7 +23,7 @@ def _parse_time(value: Any) -> datetime | None:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError:
return None
return parsed if parsed.tzinfo is not None else parsed.replace(tzinfo=timezone.utc)
return parsed if parsed.tzinfo is not None else parsed.replace(tzinfo=UTC)


def _iso(value: Any) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import secrets
import uuid
from dataclasses import dataclass
from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

from gooddata_eval._version import __version__
Expand Down Expand Up @@ -43,7 +43,7 @@
ATTR_EXPERIMENT_ITEM_METADATA_PREFIX = "langfuse.experiment.item.metadata"

_SERVICE_NAME = "gooddata-eval"
_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
_EPOCH = datetime(1970, 1, 1, tzinfo=UTC)


def new_trace_id() -> str:
Expand All @@ -57,7 +57,7 @@ def new_span_id() -> str:
def unix_nano(dt: datetime) -> str:
"""Nanoseconds since the epoch as a decimal string. Naive datetimes are treated as UTC."""
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
dt = dt.replace(tzinfo=UTC)
delta = dt - _EPOCH
nanos = (delta.days * 86400 + delta.seconds) * 1_000_000_000 + delta.microseconds * 1000
return str(nanos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import os
import sys
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from typing import TYPE_CHECKING, Any

from gooddata_eval.core.langfuse.client import HttpxLangfuseClient
Expand Down Expand Up @@ -108,7 +108,7 @@ def _resolve_run(self, report: ItemReport, dataset_item_id: str) -> ExperimentRu
)

def _build_span(self, report: ItemReport, dataset_item_id: str, run: ExperimentRun | None) -> Span:
end = datetime.now(timezone.utc)
end = datetime.now(UTC)
start = end - timedelta(seconds=report.avg_latency_s)
# "gd-eval" leads, exactly as on the agentic path, so one tag filter in Langfuse
# finds both single-shot and agentic traces this package wrote.
Expand Down
4 changes: 2 additions & 2 deletions packages/gooddata-eval/src/gooddata_eval/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

import json
import re
from enum import Enum
from enum import StrEnum
from typing import Any

from pydantic import BaseModel, ConfigDict, Field, field_validator

from gooddata_eval.core.timing import PhaseTimings


class LoopExit(str, Enum):
class LoopExit(StrEnum):
"""Why an agentic evaluator's simulated-user loop stopped.

Every agentic kind drives the agent through a loop of simulated-user turns that can end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from __future__ import annotations

from datetime import datetime, timezone
from datetime import UTC, datetime
from pathlib import Path

import orjson
Expand Down Expand Up @@ -107,7 +107,7 @@ def build_html(doc: dict, redact: bool = False, title: str = "gd-eval report") -
"""Render a merged report document into a standalone HTML page."""
payload = {
"title": title,
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC"),
"generated_at": datetime.now(UTC).strftime("%Y-%m-%d %H:%M UTC"),
"redacted": redact,
**(_redact(doc) if redact else {"runs": doc.get("runs") or {}, "comparison": doc.get("comparison") or {}}),
}
Expand Down
4 changes: 2 additions & 2 deletions packages/gooddata-eval/tests/_fake_langfuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import json
import signal
import sys
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from threading import Thread
Expand All @@ -36,7 +36,7 @@ def observation_rows(session_id: str) -> list[dict]:
"""A gen-ai-shaped root row plus two child rows for one conversation session."""
trace_id = _short_hash(session_id, 32)
root_id = _short_hash(f"{session_id}:root", 16)
start = datetime(2026, 1, 1, tzinfo=timezone.utc)
start = datetime(2026, 1, 1, tzinfo=UTC)
times = {"startTime": start.isoformat(), "endTime": (start + timedelta(seconds=_ROOT_LATENCY_SECONDS)).isoformat()}
shared = {"traceId": trace_id, "type": "GENERATION", "sessionId": session_id, **times}
root = {
Expand Down
Loading
Loading