|
20 | 20 | from __future__ import annotations |
21 | 21 |
|
22 | 22 | import copy |
| 23 | +import datetime |
| 24 | +import decimal |
23 | 25 | import functools |
24 | 26 | import itertools |
25 | 27 | import logging |
26 | 28 | import sys |
27 | 29 | import threading |
28 | 30 | import time |
29 | 31 | import types |
| 32 | +import uuid |
30 | 33 | from collections import deque |
31 | 34 | from dataclasses import dataclass |
32 | 35 | from typing import Any |
|
79 | 82 | ) |
80 | 83 |
|
81 | 84 |
|
| 85 | +_SAFE_OPAQUE_RETAINED_TYPES = ( |
| 86 | + datetime.date, |
| 87 | + datetime.datetime, |
| 88 | + datetime.time, |
| 89 | + datetime.timedelta, |
| 90 | + decimal.Decimal, |
| 91 | + uuid.UUID, |
| 92 | +) |
| 93 | + |
| 94 | + |
82 | 95 | class _RetainedChildren: |
83 | 96 | __slots__ = ("iterator",) |
84 | 97 |
|
@@ -123,29 +136,39 @@ def _retained_children(value: Any) -> Any: |
123 | 136 | if isinstance(value, deque): |
124 | 137 | return itertools.chain(deque.__iter__(value), custom) |
125 | 138 | if isinstance(value, memoryview): |
126 | | - return (value.obj,) |
| 139 | + return itertools.chain((value.obj,), custom) |
127 | 140 | if isinstance(value, functools.partial): |
128 | | - return (value.func, value.args, value.keywords) |
| 141 | + return itertools.chain((value.func, value.args, value.keywords), custom) |
129 | 142 | if isinstance(value, types.FunctionType): |
130 | 143 | closure = [] |
131 | 144 | for cell in value.__closure__ or (): |
132 | 145 | try: |
133 | 146 | closure.append(cell.cell_contents) |
134 | 147 | except ValueError: |
135 | 148 | pass |
136 | | - return itertools.chain(closure, (value.__defaults__, value.__kwdefaults__)) |
| 149 | + return itertools.chain( |
| 150 | + closure, (value.__defaults__, value.__kwdefaults__), custom |
| 151 | + ) |
137 | 152 | if isinstance(value, types.MethodType): |
138 | | - return (value.__self__, value.__func__) |
| 153 | + return itertools.chain((value.__self__, value.__func__), custom) |
139 | 154 | if isinstance(value, types.BuiltinFunctionType): |
140 | 155 | owner = value.__self__ |
141 | | - return () if owner is None or isinstance(owner, types.ModuleType) else (owner,) |
| 156 | + retained = ( |
| 157 | + () if owner is None or isinstance(owner, types.ModuleType) else (owner,) |
| 158 | + ) |
| 159 | + return itertools.chain(retained, custom) |
142 | 160 | if isinstance(value, types.MethodWrapperType): |
143 | | - return (value.__self__,) |
| 161 | + return itertools.chain((value.__self__,), custom) |
144 | 162 | if isinstance(value, types.GeneratorType): |
145 | 163 | frame = value.gi_frame |
146 | | - return () if frame is None else (frame.f_locals, value.gi_yieldfrom) |
147 | | - if isinstance(value, _ATOMIC_RETAINED_TYPES): |
148 | | - return () |
| 164 | + generator_children = ( |
| 165 | + () if frame is None else (frame.f_locals, value.gi_yieldfrom) |
| 166 | + ) |
| 167 | + return itertools.chain(generator_children, custom) |
| 168 | + if type(value) in _ATOMIC_RETAINED_TYPES: |
| 169 | + return custom |
| 170 | + if type(value) in _SAFE_OPAQUE_RETAINED_TYPES: |
| 171 | + return custom |
149 | 172 | if custom: |
150 | 173 | return custom |
151 | 174 | return _UNSUPPORTED_RETAINED_GRAPH |
@@ -672,7 +695,16 @@ def __init__( |
672 | 695 |
|
673 | 696 | def schedule(self, execution_arn: str, record: dict[str, Any]) -> None: |
674 | 697 | """Fan a canonical record out to every lane. Returns immediately.""" |
675 | | - record_size = _estimate_retained_size(record, self._max_pending_bytes) |
| 698 | + try: |
| 699 | + record_size = _estimate_retained_size(record, self._max_pending_bytes) |
| 700 | + except Exception as exc: # noqa: BLE001 - inspection must never break a hook |
| 701 | + _logger.warning( |
| 702 | + "workflow-insight: retained-size inspection failed for %s; " |
| 703 | + "rejecting this record safely: %s", |
| 704 | + execution_arn, |
| 705 | + exc, |
| 706 | + ) |
| 707 | + record_size = self._max_pending_bytes + 1 |
676 | 708 | for lane in self._lanes: |
677 | 709 | lane.schedule(execution_arn, record, record_size) |
678 | 710 |
|
|
0 commit comments