Example #1
0
    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()
Example #2
0
 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()
Example #3
0
 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()
Example #4
0
 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)
Example #5
0
 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)
Example #6
0
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
Example #7
0
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