Skip to content

Commit

Permalink
typing(interfaces): Type contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
armenzg committed Oct 8, 2024
1 parent 0213755 commit f0157f4
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 15 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ module = [
"sentry.hybridcloud.*",
"sentry.ingest.slicing",
"sentry.integrations.models.integration_feature",
"sentry.interfaces.contexts",
"sentry.issues",
"sentry.issues.analytics",
"sentry.issues.apps",
Expand Down
3 changes: 2 additions & 1 deletion src/sentry/interfaces/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def __setattr__(self, name, value):
self._data[name] = value

@classmethod
def to_python(cls, data, datapath: DataPath | None = None):
def to_python(cls: Any, data: dict[str, Any], **kwargs: Any) -> dict[str, Any] | None:
"""Creates a python interface object from the given raw data.
This function can assume fully normalized and valid data. It can create
Expand All @@ -119,6 +119,7 @@ def to_python(cls, data, datapath: DataPath | None = None):
return None

rv = cls(**data)
datapath = kwargs.pop("datapath", None)
object.__setattr__(rv, "datapath", datapath)
return rv

Expand Down
30 changes: 16 additions & 14 deletions src/sentry/interfaces/contexts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import string
from collections.abc import Iterator
from typing import Any, ClassVar, TypeVar

from django.utils.encoding import force_str
Expand All @@ -17,13 +18,13 @@


class _IndexFormatter(string.Formatter):
def format_field(self, value, format_spec):
def format_field(self, value: object, format_spec: str) -> str:
if not format_spec and isinstance(value, bool):
return value and "yes" or "no"
return string.Formatter.format_field(self, value, format_spec)


def format_index_expr(format_string, data):
def format_index_expr(format_string: str, data: dict[str, object]) -> str:
return str(_IndexFormatter().vformat(str(format_string), (), data).strip())


Expand Down Expand Up @@ -82,7 +83,7 @@ class ContextType:
type: str
"""This should match the `type` key in context object"""

def __init__(self, alias, data):
def __init__(self, alias: str, data: dict[str, object]) -> None:
self.alias = alias
ctx_data = {}
for key, value in data.items():
Expand All @@ -98,30 +99,31 @@ def __init__(self, alias, data):
ctx_data[force_str(key)] = self.change_type(value)
self.data = ctx_data

def to_json(self):
def to_json(self) -> dict[str, object]:
rv = dict(self.data)
rv["type"] = self.type
return prune_empty_keys(rv)

@classmethod
def values_for_data(cls, data):
def values_for_data(cls, data: dict[str, Any]) -> list[dict[str, object]]:
rv = []
for context in (data.get("contexts") or {}).values():
if context and context.get("type") == cls.type:
rv.append(context)
return rv

@classmethod
def primary_value_for_data(cls, data):
def primary_value_for_data(cls: Any, data: dict[str, object]) -> dict[str, object] | None:
val = get_path(data, "contexts", cls.type)
if val and val.get("type") == cls.type:
return val

rv = cls.values_for_data(data)
if len(rv) == 1:
return rv[0]
return None

def iter_tags(self):
def iter_tags(self) -> Iterator[tuple[str, str]]:
if self.context_to_tag_mapping:
for field, f_string in self.context_to_tag_mapping.items():
try:
Expand All @@ -134,7 +136,7 @@ def iter_tags(self):
else:
yield (f"{self.alias}.{field}", value)

def change_type(self, value: int | float | list | dict) -> Any:
def change_type(self, value: int | float | list[Any] | dict[str, Any]) -> object:
if isinstance(value, (float, int)) and len(str_value := force_str(value)) > 15:
return str_value
if isinstance(value, list):
Expand Down Expand Up @@ -217,7 +219,7 @@ class Contexts(Interface):
score = 800

@classmethod
def to_python(cls, data, **kwargs):
def to_python(cls: Any, data: dict[str, Any], **kwargs: Any) -> dict[str, Any] | None:
rv = {}

# Note the alias is the key of the context entry
Expand All @@ -230,20 +232,20 @@ def to_python(cls, data, **kwargs):
return super().to_python(rv, **kwargs)

@classmethod
def normalize_context(cls, alias, data):
def normalize_context(cls: Any, alias: str, data: dict[str, Any]) -> ContextType:
ctx_type = data.get("type", alias)
ctx_cls = context_types.get(ctx_type, DefaultContextType)
return ctx_cls(alias, data)

def iter_contexts(self):
return self._data.values()
def iter_contexts(self) -> list[ContextType]:
return list(self._data.values())

def to_json(self):
def to_json(self) -> dict[str, dict[str, object]]:
rv = {}
for alias, inst in self._data.items():
rv[alias] = inst.to_json()
return rv

def iter_tags(self):
def iter_tags(self) -> Iterator[tuple[str, str]]:
for inst in self.iter_contexts():
yield from inst.iter_tags()

0 comments on commit f0157f4

Please sign in to comment.