Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion msgspec/_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,9 @@ Raw_copy(Raw *self, PyObject *unused)
}
PyObject *buf = PyBytes_FromStringAndSize(self->buf, self->len);
if (buf == NULL) return NULL;
return Raw_New(buf);
PyObject *out = Raw_New(buf);
Py_DECREF(buf);
return out;
}

static PyMethodDef Raw_methods[] = {
Expand Down
25 changes: 25 additions & 0 deletions tests/test_raw.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import operator
import subprocess
import sys
import textwrap
import weakref

import pytest
Expand Down Expand Up @@ -69,6 +71,29 @@ def test_raw_copy():
assert ref() is None


def test_raw_copy_doesnt_leak():
"""See https://github.com/jcrist/msgspec/pull/709"""
script = textwrap.dedent(
"""
import msgspec
import tracemalloc

tracemalloc.start()

raw = msgspec.Raw(bytearray(1000))
for _ in range(10000):
raw.copy()

_, peak = tracemalloc.get_traced_memory()
print(peak)
"""
)

output = subprocess.check_output([sys.executable, "-c", script])
peak = int(output.decode().strip())
assert peak < 10_000 # should really be ~2000


def test_raw_pickle_bytes():
orig_buffer = b"test"
r = msgspec.Raw(orig_buffer)
Expand Down
Loading