|
18 | 18 | from functools import cached_property |
19 | 19 | from random import Random |
20 | 20 | from sys import float_info |
21 | | -from types import ModuleType |
22 | 21 | from typing import ( |
23 | 22 | TYPE_CHECKING, |
24 | 23 | Any, |
|
274 | 273 | # are all modules, not necessarily local ones. This lets us quickly see which |
275 | 274 | # modules are new without an expensive path.resolve() or is_local_module_file |
276 | 275 | # cache lookup. |
277 | | -_seen_modules: set[ModuleType] = set() |
| 276 | +# We track by module object when hashable, falling back to the module name |
| 277 | +# (str key in sys.modules) for unhashable entries like SimpleNamespace. |
| 278 | +_seen_modules: set = set() |
278 | 279 | _sys_modules_len: int | None = None |
279 | 280 |
|
280 | 281 |
|
@@ -310,20 +311,28 @@ def _get_local_constants() -> Constants: |
310 | 311 | # careful: store sys.modules length when we first check to avoid race conditions |
311 | 312 | # with other threads loading a module before we set _sys_modules_len. |
312 | 313 | if (sys_modules_len := len(sys.modules)) != _sys_modules_len: |
313 | | - # set(_seen_modules) shouldn't typically be required, but I have run into |
314 | | - # a "set changed size during iteration" error here when running |
315 | | - # test_provider_conformance_crosshair. |
316 | | - new_modules = set(sys.modules.values()) - set(_seen_modules) |
| 314 | + new_modules = [] |
| 315 | + for name, module in list(sys.modules.items()): |
| 316 | + try: |
| 317 | + seen = module in _seen_modules |
| 318 | + except TypeError: |
| 319 | + # unhashable module (e.g. SimpleNamespace); fall back to name |
| 320 | + seen = name in _seen_modules |
| 321 | + if not seen: |
| 322 | + new_modules.append((name, module)) |
317 | 323 | # Repeated SortedSet unions are expensive. Do the initial unions on a |
318 | 324 | # set(), then do a one-time union with _local_constants after. |
319 | 325 | new_constants = Constants() |
320 | | - for module in new_modules: |
| 326 | + for name, module in new_modules: |
321 | 327 | if ( |
322 | 328 | module_file := getattr(module, "__file__", None) |
323 | 329 | ) is not None and is_local_module_file(module_file): |
324 | 330 | new_constants |= constants_from_module(module) |
| 331 | + try: |
| 332 | + _seen_modules.add(module) |
| 333 | + except TypeError: |
| 334 | + _seen_modules.add(name) |
325 | 335 | _local_constants |= new_constants |
326 | | - _seen_modules.update(new_modules) |
327 | 336 | _sys_modules_len = sys_modules_len |
328 | 337 |
|
329 | 338 | # if we add any new constant, invalidate the constant cache for permitted values. |
|
0 commit comments