# -*- coding: utf-8 -*-
import sys
from uuid import uuid4
import pytest
from pandas.compat import PY3, intern
from pandas.util._move import BadMove, move_into_mutable_buffer, stolenbuf
def test_cannot_create_instance_of_stolen_buffer():
# Stolen buffers need to be created through the smart constructor
# "move_into_mutable_buffer," which has a bunch of checks in it.
msg = "cannot create 'pandas.util._move.stolenbuf' instances"
with pytest.raises(TypeError, match=msg):
stolenbuf()
def test_more_than_one_ref():
# Test case for when we try to use "move_into_mutable_buffer"
# when the object being moved has other references.
b = b"testing"
with pytest.raises(BadMove, match="testing") as e:
def handle_success(type_, value, tb):
assert value.args[0] is b
return type(e).handle_success(e, type_, value, tb) # super
e.handle_success = handle_success
move_into_mutable_buffer(b)
def test_exactly_one_ref():
# Test case for when the object being moved has exactly one reference.
b = b"testing"
# We need to pass an expression on the stack to ensure that there are
# not extra references hanging around. We cannot rewrite this test as
# buf = b[:-3]
# as_stolen_buf = move_into_mutable_buffer(buf)
# because then we would have more than one reference to buf.
as_stolen_buf = move_into_mutable_buffer(b[:-3])
# Materialize as byte-array to show that it is mutable.
assert bytearray(as_stolen_buf) == b"test"
@pytest.mark.skipif(PY3, reason="bytes objects cannot be interned in PY3")
def test_interned():
salt = uuid4().hex
def make_string():
# We need to actually create a new string so that it has refcount
# one. We use a uuid so that we know the string could not already
# be in the intern table.
return "".join(("testing: ", salt))
# This should work, the string has one reference on the stack.
move_into_mutable_buffer(make_string())
refcount = [None] # nonlocal
def ref_capture(ob):
# Subtract two because those are the references owned by this frame:
# 1. The local variables of this stack frame.
# 2. The python data stack of this stack frame.
refcount[0] = sys.getrefcount(ob) - 2
return ob
with pytest.raises(BadMove, match="testing"):
# If we intern the string, it will still have one reference. Now,
# it is in the intern table, so if other people intern the same
# string while the mutable buffer holds the first string they will
# be the same instance.
move_into_mutable_buffer(ref_capture(intern(make_string()))) # noqa
assert refcount[0] == 1