mirror of
https://github.com/simonw/datasette.git
synced 2026-09-29 13:24:07 +02:00
Make registry entries deepcopy-able, so console metric dumps work
Attribute, SpanName and MetricName are str subclasses whose __new__ requires the metadata arguments, so copy.deepcopy could not reconstruct one - it falls back to cls.__new__(cls) and raises TypeError. That broke a real path rather than a theoretical one. The SDK's ConsoleMetricExporter renders data points through dataclasses.asdict(), which deepcopies mappings, and both core and kit-based plugins pass registry entries as metric attribute keys - so every console metrics dump crashed, core's own points included. Found by datasette-paper's dev harness running opentelemetry-instrument with console exporters. __reduce__ collapses copies to a plain str, which is what an entry is everywhere except the registry module itself: the description, values and buckets describe the single registered instance, and nothing reads them off a copy. Pickle is fixed by the same change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JwU7BcTnAxUGSJYhBrQaY7
This commit is contained in:
parent
3f16fcb3da
commit
bbc85b7e4f
2 changed files with 72 additions and 1 deletions
|
|
@ -50,6 +50,20 @@ class Attribute(str):
|
|||
self.values = frozenset(values) if values is not None else None
|
||||
return self
|
||||
|
||||
def __reduce__(self):
|
||||
# Copies and pickles collapse to a plain str. Without this, `copy` has
|
||||
# to reconstruct a str subclass through `cls.__new__(cls)`, which these
|
||||
# classes reject - their `__new__` requires the metadata arguments. It
|
||||
# is not a theoretical problem: the SDK's ConsoleMetricExporter renders
|
||||
# data points with `dataclasses.asdict()`, which deepcopies mappings,
|
||||
# and registry entries are used as metric attribute keys - so every
|
||||
# console metrics dump would crash. Collapsing is also the honest
|
||||
# answer, not a workaround. On the wire and in a copy an entry *is*
|
||||
# its string; the description, values and buckets describe the single
|
||||
# registered instance in this module, and nothing reads them off a
|
||||
# copy.
|
||||
return (str, (str(self),))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Attribute({str(self)!r})"
|
||||
|
||||
|
|
@ -95,6 +109,10 @@ class SpanName(str):
|
|||
self.kind = kind
|
||||
return self
|
||||
|
||||
def __reduce__(self):
|
||||
# See Attribute.__reduce__.
|
||||
return (str, (str(self),))
|
||||
|
||||
def __repr__(self):
|
||||
return f"SpanName({str(self)!r})"
|
||||
|
||||
|
|
@ -117,6 +135,10 @@ class MetricName(str):
|
|||
self.buckets = tuple(buckets) if buckets is not None else None
|
||||
return self
|
||||
|
||||
def __reduce__(self):
|
||||
# See Attribute.__reduce__.
|
||||
return (str, (str(self),))
|
||||
|
||||
def __repr__(self):
|
||||
return f"MetricName({str(self)!r})"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue