def test_cache(self): import _abc class SomeABC: pass token_before = _abc.get_cache_token() assert _abc.get_cache_token() == token_before _abc._abc_init(SomeABC) assert _abc.get_cache_token() == token_before class SomeConcreteSubClass: pass _abc._abc_register(SomeABC, SomeConcreteSubClass) assert _abc.get_cache_token() != token_before registry, cache, negative_cache, negative_cache_version = _abc._get_dump( SomeABC) assert len(registry) == 1 assert len(cache) == 0 assert len(negative_cache) == 0 assert negative_cache_version == token_before assert _abc._abc_subclasscheck(SomeABC, SomeConcreteSubClass) registry, cache, negative_cache, negative_cache_version = _abc._get_dump( SomeABC) assert len(registry) == 1 assert len(cache) == 1 assert len(negative_cache) == 0 assert negative_cache_version == _abc.get_cache_token() class SomeOtherClass: pass assert not _abc._abc_subclasscheck(SomeABC, SomeOtherClass) registry, cache, negative_cache, negative_cache_version = _abc._get_dump( SomeABC) assert len(registry) == 1 assert len(cache) == 1 assert len(negative_cache) == 1 assert negative_cache_version == _abc.get_cache_token() _abc._reset_caches(SomeABC) registry, cache, negative_cache, negative_cache_version = _abc._get_dump( SomeABC) assert len(registry) == 1 assert len(cache) == 0 assert len(negative_cache) == 0 assert negative_cache_version == _abc.get_cache_token() _abc._reset_registry(SomeABC) registry, cache, negative_cache, negative_cache_version = _abc._get_dump( SomeABC) assert len(registry) == 0 assert len(cache) == 0 assert len(negative_cache) == 0 assert negative_cache_version == _abc.get_cache_token()
def getRegisteredABCs(cls): import _abc dump = _abc._get_dump(cls) for cls_ref in dump[0]: # dereference the weakref, # since this is a normal set of weakrefs, # not a weak set yield cls_ref()
def _dump_registry(cls, file=None): """Debug helper to print the ABC registry.""" print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file) print(f"Inv. counter: {get_cache_token()}", file=file) (_abc_registry, _abc_cache, _abc_negative_cache, _abc_negative_cache_version) = _get_dump(cls) print(f"_abc_registry: {_abc_registry!r}", file=file) print(f"_abc_cache: {_abc_cache!r}", file=file) print(f"_abc_negative_cache: {_abc_negative_cache!r}", file=file) print(f"_abc_negative_cache_version: {_abc_negative_cache_version!r}", file=file)
def dash_R(the_module, test, indirect_test, huntrleaks): """Run a test multiple times, looking for reference leaks. Returns: False if the test didn't leak references; True if we detected refleaks. """ # This code is hackish and inelegant, but it seems to do the job. import copyreg import collections.abc if not hasattr(sys, 'gettotalrefcount'): raise Exception("Tracking reference leaks requires a debug build " "of Python") # Save current values for dash_R_cleanup() to restore. fs = warnings.filters[:] ps = copyreg.dispatch_table.copy() pic = sys.path_importer_cache.copy() try: import zipimport except ImportError: zdc = None # Run unmodified on platforms without zipimport support else: zdc = zipimport._zip_directory_cache.copy() abcs = {} for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: if not isabstract(abc): continue for obj in abc.__subclasses__() + [abc]: abcs[obj] = _get_dump(obj)[0] # bpo-31217: Integer pool to get a single integer object for the same # value. The pool is used to prevent false alarm when checking for memory # block leaks. Fill the pool with values in -1000..1000 which are the most # common (reference, memory block, file descriptor) differences. int_pool = {value: value for value in range(-1000, 1000)} def get_pooled_int(value): return int_pool.setdefault(value, value) nwarmup, ntracked, fname = huntrleaks fname = os.path.join(support.SAVEDCWD, fname) repcount = nwarmup + ntracked rc_deltas = [0] * repcount alloc_deltas = [0] * repcount fd_deltas = [0] * repcount print("beginning", repcount, "repetitions", file=sys.stderr) print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, flush=True) # initialize variables to make pyflakes quiet rc_before = alloc_before = fd_before = 0 for i in range(repcount): indirect_test() alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc, abcs) print('.', end='', file=sys.stderr, flush=True) if i >= nwarmup: rc_deltas[i] = get_pooled_int(rc_after - rc_before) alloc_deltas[i] = get_pooled_int(alloc_after - alloc_before) fd_deltas[i] = get_pooled_int(fd_after - fd_before) alloc_before = alloc_after rc_before = rc_after fd_before = fd_after print(file=sys.stderr) # These checkers return False on success, True on failure def check_rc_deltas(deltas): # Checker for reference counters and memomry blocks. # # bpo-30776: Try to ignore false positives: # # [3, 0, 0] # [0, 1, 0] # [8, -8, 1] # # Expected leaks: # # [5, 5, 6] # [10, 1, 1] return all(delta >= 1 for delta in deltas) def check_fd_deltas(deltas): return any(deltas) failed = False for deltas, item_name, checker in [ (rc_deltas, 'references', check_rc_deltas), (alloc_deltas, 'memory blocks', check_rc_deltas), (fd_deltas, 'file descriptors', check_fd_deltas) ]: # ignore warmup runs deltas = deltas[nwarmup:] if checker(deltas): msg = '%s leaked %s %s, sum=%s' % ( test, deltas, item_name, sum(deltas)) print(msg, file=sys.stderr, flush=True) with open(fname, "a") as refrep: print(msg, file=refrep) refrep.flush() failed = True return failed