Skip to content
Open
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
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Code contributions:
- Jesper Schlegel (jesperschlegel)
- J vanBemmel (jbemmel)
- m-janicki
- Abhinav Maurya (Fire162)


Suggestions and bug reporting:
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Changelog
=========

* Fixing #344 Frozen BoxList instances cannot be pickled (thanks to Abhinav Maurya)

Version 7.4.1
-------------

Expand Down
10 changes: 5 additions & 5 deletions box/box_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
_list_pos_re = re.compile(r"\[(\d+)\]")


def _frozen_box_list_method(*args, **kwargs):
raise BoxError("BoxList is frozen")


class BoxList(list):
"""
Drop in replacement of list, that converts added objects to Box or BoxList
Expand All @@ -58,12 +62,8 @@ def __init__(self, iterable: Iterable | None = None, box_class: type[box.Box] =
self.append(x)
self.box_org_ref = None
if box_options.get("frozen_box"):

def frozen(*args, **kwargs):
raise BoxError("BoxList is frozen")

for method in ["append", "extend", "insert", "pop", "remove", "reverse", "sort"]:
self.__setattr__(method, frozen)
self.__setattr__(method, _frozen_box_list_method)

def __getitem__(self, item):
if self.box_options.get("box_dots") and isinstance(item, str) and item.startswith("["):
Expand Down
49 changes: 49 additions & 0 deletions test/test_box_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import json
import os
import pickle
import shutil
import sys
import platform
Expand Down Expand Up @@ -74,6 +75,54 @@ def test_frozen_list(self):
bl2[1] = 4
assert bl2[1] == 4

def test_frozen_list_pickle(self):
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
bl = BoxList([5, 4, 3], frozen_box=True)
dumped = pickle.dumps(bl, protocol=protocol)
loaded = pickle.loads(dumped)
assert loaded == [5, 4, 3]
assert loaded.box_options.get("frozen_box") is True
with pytest.raises(BoxError):
loaded.append(2)
with pytest.raises(BoxError):
loaded.extend([1])
with pytest.raises(BoxError):
loaded.pop(0)
with pytest.raises(BoxError):
loaded.remove(5)
with pytest.raises(BoxError):
loaded.insert(0, 9)
with pytest.raises(BoxError):
loaded.reverse()
with pytest.raises(BoxError):
loaded.sort()
with pytest.raises(BoxError):
del loaded[0]
with pytest.raises(BoxError):
loaded[0] = 99

empty_bl = BoxList([], frozen_box=True)
empty_dumped = pickle.dumps(empty_bl, protocol=protocol)
empty_loaded = pickle.loads(empty_dumped)
assert empty_loaded == []
assert empty_loaded.box_options.get("frozen_box") is True
with pytest.raises(BoxError):
empty_loaded.append(1)

def test_box_list_pickle(self):
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
bl = BoxList([1, 2, 3])
dumped = pickle.dumps(bl, protocol=protocol)
loaded = pickle.loads(dumped)
assert loaded == [1, 2, 3]
assert not loaded.box_options.get("frozen_box")
loaded.append(4)
assert loaded == [1, 2, 3, 4]
loaded[0] = 9
assert loaded[0] == 9
del loaded[0]
assert loaded == [2, 3, 4]

def test_box_list_to_json(self):
bl = BoxList([{"item": 1, "CamelBad": 2}])
assert json.loads(bl.to_json())[0]["item"] == 1
Expand Down