Refcursed

A library for the things you can, but should not, do with refcounts

Refcursed is a library for the things you can, but should not, do with the ability to determine the number of current references to any object. At import time, the library emits a warning because really what are you doing.

§Use Cases

§Bringing balance to the universe

Did you know that the Python runtime knows how many references to True and False exist at any given time?

Have you ever wanted to bring balance to your programs and know that they're just as True as they are False?

Look no further! Refcursed provides one simple function you can call to right the universe.

import refcursed
refcursed.balance_bool()

§Unit testing

Have you ever wanted to make sure that code you're calling isn't creating extra aliases to a certain value?

Now you can! With the new expect_ref_delta context manager (compare to pytest.raises), you can right unit tests that ensure exactly the amount of references you expect to be added/removed are.

from refcursed import expect_ref_delta

my_list = []
with expect_ref_delta(201, delta=2):
    my_list.append(201)
    my_list.append(201)

If you want to check multiple at the same time, we even have a pluralized version.

from refcursed import expect_ref_deltas

my_list = []
with expect_ref_deltas([201, 220], delta=1):
    my_list.append(201)
    my_list.append(220)

§Counting sort but better worse

The thing about counting sort is that you have to have counters for your different values. This is really silly though, because the runtime has counters for all of them anyway, the refcount. Our state of the art counting sort implementation uses those counters instead of its own for... performance?

import refcursed
result = refcursed.refcounting_sort([4, 4, 4, 3, 2, 1, 1])
assert result = [1, 1, 2, 3, 4, 4, 4]

§Counting values

The same mechanism that lets us perform counting sort, taking refcounts before and after deleting things, allows us to count arbitrary lists contents as well.

import refcursed
result = refcursed.count_values([4, 4, 4, 3, 2, 1, 1])
assert result == {
    1: 2,
    2: 1,
    3: 1,
    4: 3
}

§Comparing and Sorting by refcount

The library also provides utilities for comparing the refcounts of two values and sorting sequences of values by refcount.

import refcursed
assert refcursed.sort_refcount([1, 2, 3]) == ... # varies
assert refcursed.compare_refcounts(1, 2) == ... #varies

See the Integers section to find out why these are subtly non-trivial.

§Considerations, Caveats, and Curiosities

Most of the characteristics of sys.getrefcount are intuitive and you might guess, but others you may not.

§str Interning

Strings in Python sometimes act as separate objects that don't is compare true and don't have the same refcount, but some are the same and do. The latter case is due to interning, where Python stores one str object for multiple (potentially independent) instances of the same string value.

Interning sometimes happens automatically, like in all string literals, and can also be done manual, using sys.intern. Be aware of when interning is/isn't happening and how that may affect the behavior of refcounting.

§int and bool Interning

Similarly to strings, integers and boolean values are also interned but following different rules. Any time an integer (within some size limit) is returned by an arithmetic operation, Python pulls the correct integer object from a table instead of creating a new one incrementing the count for that integer. Booleans work much the same way but are always interned because there are only two possible values.

One fun quirk of this is that the integers returned by sys.getrefcount can be interned and increment the refcount for that integer. Under very specific circumstances this can change the outcome of a comparison.

# If B is equal to the integer that is the refcount of A
# and B is a small enough integer to be automatically interned
# and the refcount of B is one less than the refcount of A,
# then even though A had a larger refcount the act of observing them
# has made them equal and this condition will fail.
assert sys.getrefcount(A) > sys.getrefcount(B)

This may sound very precise and very unlikely, but it really isn't. It can easily occur whenever sorting or comparing small integer values by refcount. For robustness, all comparisons in the refcursed library either use float(sys.getrefcount(...)) to store refcounts without modifying others or compensate for the case above.