Skip to content
Merged
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

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/checkup/executor/metric_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from checkup.metric import ExecutorType, Measurement, Metric
from checkup.provider import Provider
from checkup.providers.tags import TagProvider

logger = logging.getLogger(__name__)

Expand All @@ -40,14 +41,14 @@ def calculate(
metrics: list[Metric],
execution_order: list[type[Metric]],
context: dict[str, Any],
tags: dict[str, Any],
provided_classes: set[type[Provider]],
failed_providers: dict[type[Provider], ProviderError] | None = None,
) -> list[Measurement]:
"""
Calculate all metrics in execution order.
"""

tags = context.get(TagProvider.name, {})
state = CalculationState(
context=context,
tags=tags,
Expand Down
15 changes: 4 additions & 11 deletions src/checkup/executor/provider_executor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Provider execution."""

import logging
from typing import Any

from checkup.errors import ProviderError
from checkup.provider import Provider
Expand All @@ -17,36 +16,30 @@ class ProviderExecutor:

def execute(
self, provider_set: list[Provider]
) -> tuple[Context, dict[str, Any], list[ProviderError]]:
) -> tuple[Context, list[ProviderError]]:
"""
Execute all providers and build namespaced context.

Each provider's data is added under its namespace (provider.name).
Providers implementing is_tag_provider() have their data returned
separately for merging into metric tags.

Args:
provider_set: List of provider instances

Returns:
Tuple of (context dict, tags dict, list of errors)
Tuple of (context dict, list of errors)
"""

context: Context = {}
tags: dict[str, Any] = {}
errors: list[ProviderError] = []

for provider in provider_set:
try:
logger.debug("Executing provider: %s", provider.name)
data = provider.provide()
context[provider.name] = data
if provider.is_tag_provider():
tags.update(data)
context[provider.name] = provider.provide()
logger.debug("Provider %s completed successfully", provider.name)
except Exception as e:
error = ProviderError(provider, e)
logger.error("Provider %s failed: %s", provider.name, e)
errors.append(error)

return context, tags, errors
return context, errors
13 changes: 2 additions & 11 deletions src/checkup/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,13 @@ def _measure_single_provider_set(
ProviderError: If a provider fails during execution
"""

context, tags, errors = ProviderExecutor().execute(provider_set)
context, errors = ProviderExecutor().execute(provider_set)
failed_providers = {type(e.provider): e for e in errors}

return MetricCalculator().calculate(
metrics,
execution_order,
context,
tags,
{type(p) for p in provider_set},
failed_providers,
)
Expand Down Expand Up @@ -187,15 +186,7 @@ def measure(
if all_errors:
failed_contexts = []
for ps, _ in all_errors:
tags = {
k: v
for p in ps
if p.is_tag_provider()
for k, v in p.provide().items()
}
failed_contexts.append(
tags if tags else {"providers": [p.name for p in ps]}
)
failed_contexts.append({"providers": [p.name for p in ps]})
failed_contexts_str = "\n ".join(str(ctx) for ctx in failed_contexts)
logger.info(
"Measurement complete: %d measurements, %d failed contexts:\n %s",
Expand Down
13 changes: 0 additions & 13 deletions src/checkup/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,3 @@ def provide(self) -> dict[str, Any]:
Dict of data to add under context[cls.name]
"""
...

def is_tag_provider(self) -> bool:
"""Return True if this provider supplies tags instead of context data.

Tag providers have their data merged directly into metric.tags
instead of being added to the context namespace.

Override this method to return True in tag-providing subclasses.

Returns:
False by default; True for tag providers
"""
return False
20 changes: 3 additions & 17 deletions src/checkup/providers/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@


class TagProvider(Provider):
"""Special provider for adding tags to metrics.
"""
Provider for adding tags to metrics.

Unlike regular providers, TagProvider's data is auto-merged
into metric.tags by the framework rather than added to context.
Tags are automatically merged into measurement.tags after calculation.

Example:
hub.with_providers([
Expand All @@ -20,21 +20,7 @@ class TagProvider(Provider):
name: ClassVar[str] = "tags"

def __init__(self, **tags: Any):
"""Initialize with arbitrary key-value tags.

Args:
**tags: Key-value pairs to add as metric tags
"""
self.tags = tags

def provide(self) -> dict[str, Any]:
"""Return tags dict.

Returns:
Dict of tags to merge into metric.tags
"""
return self.tags

def is_tag_provider(self) -> bool:
"""Return True since this is a tag provider."""
return True