Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
vllm_cscc
Commits
ada6f91d
Unverified
Commit
ada6f91d
authored
Jan 06, 2026
by
Nikhil G
Committed by
GitHub
Jan 06, 2026
Browse files
Fix RecursionError in MediaWithBytes unpickling (#31191)
Signed-off-by:
Nikhil Ghosh
<
nikhil@anyscale.com
>
parent
8becf146
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
39 additions
and
1 deletion
+39
-1
tests/multimodal/test_image.py
tests/multimodal/test_image.py
+33
-0
tools/pre_commit/check_pickle_imports.py
tools/pre_commit/check_pickle_imports.py
+1
-0
vllm/multimodal/base.py
vllm/multimodal/base.py
+5
-1
No files found.
tests/multimodal/test_image.py
View file @
ada6f91d
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import
pickle
from
pathlib
import
Path
import
numpy
as
np
import
pytest
from
PIL
import
Image
,
ImageChops
from
vllm.multimodal.base
import
MediaWithBytes
from
vllm.multimodal.image
import
ImageMediaIO
,
convert_image_mode
pytestmark
=
pytest
.
mark
.
cpu_test
...
...
@@ -157,3 +159,34 @@ def test_rgba_background_color_validation():
ImageMediaIO
(
rgba_background_color
=
(
0
,
0
,
0
))
# Should not raise
ImageMediaIO
(
rgba_background_color
=
[
255
,
255
,
255
])
# Should not raise
ImageMediaIO
(
rgba_background_color
=
(
128
,
128
,
128
))
# Should not raise
def
test_media_with_bytes_pickle_roundtrip
():
"""Regression test for pickle/unpickle of MediaWithBytes.
Verifies that MediaWithBytes can be pickled and unpickled without
RecursionError. See: https://github.com/vllm-project/vllm/issues/30818
"""
original_image
=
Image
.
open
(
ASSETS_DIR
/
"image1.png"
).
convert
(
"RGB"
)
original_bytes
=
b
"test_bytes_data"
wrapper
=
MediaWithBytes
(
media
=
original_image
,
original_bytes
=
original_bytes
)
# Verify attribute delegation works before pickling
assert
wrapper
.
width
==
original_image
.
width
assert
wrapper
.
height
==
original_image
.
height
assert
wrapper
.
mode
==
original_image
.
mode
# Pickle and unpickle (this would cause RecursionError before the fix)
pickled
=
pickle
.
dumps
(
wrapper
)
unpickled
=
pickle
.
loads
(
pickled
)
# Verify the unpickled object works correctly
assert
unpickled
.
original_bytes
==
original_bytes
assert
unpickled
.
media
.
width
==
original_image
.
width
assert
unpickled
.
media
.
height
==
original_image
.
height
# Verify attribute delegation works after unpickling
assert
unpickled
.
width
==
original_image
.
width
assert
unpickled
.
height
==
original_image
.
height
assert
unpickled
.
mode
==
original_image
.
mode
tools/pre_commit/check_pickle_imports.py
View file @
ada6f91d
...
...
@@ -27,6 +27,7 @@ ALLOWED_FILES = {
"vllm/distributed/device_communicators/shm_broadcast.py"
,
"vllm/distributed/device_communicators/shm_object_storage.py"
,
"vllm/utils/hashing.py"
,
"tests/multimodal/test_image.py"
,
"tests/tokenizers_/test_hf.py"
,
"tests/utils_/test_hashing.py"
,
"benchmarks/kernels/graph_machete_bench.py"
,
...
...
vllm/multimodal/base.py
View file @
ada6f91d
...
...
@@ -34,7 +34,11 @@ class MediaWithBytes(Generic[_T]):
def
__getattr__
(
self
,
name
:
str
):
"""Delegate attribute access to the underlying media object."""
# This is only called when the attribute is not found on self
# Guard against recursion during unpickling when media isn't set yet.
# pickle creates objects without calling __init__, so self.media may
# not exist when __getattr__ is called for methods like __setstate__.
if
"media"
not
in
self
.
__dict__
:
raise
AttributeError
(
name
)
return
getattr
(
self
.
media
,
name
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment