|
21 | 21 | from __future__ import annotations |
22 | 22 |
|
23 | 23 | import copy |
| 24 | +import datetime |
| 25 | +import decimal |
24 | 26 | import functools |
25 | 27 | import itertools |
26 | 28 | import logging |
27 | 29 | import sys |
28 | 30 | import threading |
29 | 31 | import time |
30 | 32 | import types |
| 33 | +import uuid |
31 | 34 | from collections import OrderedDict, deque |
32 | 35 | from typing import Any |
33 | 36 |
|
|
69 | 72 | ) |
70 | 73 |
|
71 | 74 |
|
| 75 | +_SAFE_OPAQUE_RETAINED_TYPES = ( |
| 76 | + datetime.date, |
| 77 | + datetime.datetime, |
| 78 | + datetime.time, |
| 79 | + datetime.timedelta, |
| 80 | + decimal.Decimal, |
| 81 | + uuid.UUID, |
| 82 | +) |
| 83 | + |
| 84 | + |
72 | 85 | class _RetainedChildren: |
73 | 86 | __slots__ = ("iterator",) |
74 | 87 |
|
@@ -113,29 +126,39 @@ def _retained_children(value: Any) -> Any: |
113 | 126 | if isinstance(value, deque): |
114 | 127 | return itertools.chain(deque.__iter__(value), custom) |
115 | 128 | if isinstance(value, memoryview): |
116 | | - return (value.obj,) |
| 129 | + return itertools.chain((value.obj,), custom) |
117 | 130 | if isinstance(value, functools.partial): |
118 | | - return (value.func, value.args, value.keywords) |
| 131 | + return itertools.chain((value.func, value.args, value.keywords), custom) |
119 | 132 | if isinstance(value, types.FunctionType): |
120 | 133 | closure = [] |
121 | 134 | for cell in value.__closure__ or (): |
122 | 135 | try: |
123 | 136 | closure.append(cell.cell_contents) |
124 | 137 | except ValueError: |
125 | 138 | pass |
126 | | - return itertools.chain(closure, (value.__defaults__, value.__kwdefaults__)) |
| 139 | + return itertools.chain( |
| 140 | + closure, (value.__defaults__, value.__kwdefaults__), custom |
| 141 | + ) |
127 | 142 | if isinstance(value, types.MethodType): |
128 | | - return (value.__self__, value.__func__) |
| 143 | + return itertools.chain((value.__self__, value.__func__), custom) |
129 | 144 | if isinstance(value, types.BuiltinFunctionType): |
130 | 145 | owner = value.__self__ |
131 | | - return () if owner is None or isinstance(owner, types.ModuleType) else (owner,) |
| 146 | + retained = ( |
| 147 | + () if owner is None or isinstance(owner, types.ModuleType) else (owner,) |
| 148 | + ) |
| 149 | + return itertools.chain(retained, custom) |
132 | 150 | if isinstance(value, types.MethodWrapperType): |
133 | | - return (value.__self__,) |
| 151 | + return itertools.chain((value.__self__,), custom) |
134 | 152 | if isinstance(value, types.GeneratorType): |
135 | 153 | frame = value.gi_frame |
136 | | - return () if frame is None else (frame.f_locals, value.gi_yieldfrom) |
137 | | - if isinstance(value, _ATOMIC_RETAINED_TYPES): |
138 | | - return () |
| 154 | + generator_children = ( |
| 155 | + () if frame is None else (frame.f_locals, value.gi_yieldfrom) |
| 156 | + ) |
| 157 | + return itertools.chain(generator_children, custom) |
| 158 | + if type(value) in _ATOMIC_RETAINED_TYPES: |
| 159 | + return custom |
| 160 | + if type(value) in _SAFE_OPAQUE_RETAINED_TYPES: |
| 161 | + return custom |
139 | 162 | if custom: |
140 | 163 | return custom |
141 | 164 | return _UNSUPPORTED_RETAINED_GRAPH |
@@ -568,7 +591,16 @@ def __init__( |
568 | 591 |
|
569 | 592 | def schedule(self, execution_arn: str, record: dict[str, Any]) -> None: |
570 | 593 | """Fan a canonical record out to every lane. Returns immediately.""" |
571 | | - record_size = _estimate_retained_size(record, self._max_pending_bytes) |
| 594 | + try: |
| 595 | + record_size = _estimate_retained_size(record, self._max_pending_bytes) |
| 596 | + except Exception as exc: # noqa: BLE001 - inspection must never break a hook |
| 597 | + _logger.warning( |
| 598 | + "workflow-insight: retained-size inspection failed for %s; " |
| 599 | + "rejecting this record safely: %s", |
| 600 | + execution_arn, |
| 601 | + exc, |
| 602 | + ) |
| 603 | + record_size = self._max_pending_bytes + 1 |
572 | 604 | for lane in self._lanes: |
573 | 605 | lane.schedule(execution_arn, record, record_size) |
574 | 606 |
|
|
0 commit comments