#๐Ÿ”’ Urwid and Pickle Incompatibility?

22 messages ยท Page 1 of 1 (latest)

queen tiger
#

I'm using pickle in my project to save a dictionary that doesn't contain any UI code or any references to urwid, but I get this error when I try to save.

Can't get local object 'MonitoredFocusList.__init__.<locals>.<lambda>'

But I can't find anything in the reference that mentions what a MonitoredFocusList is. Any help is appreciated, thanks!

signal sundialBOT
#

@queen tiger

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

queen tiger
#

The objects in the dictionary use an event system to tell the top-level UI to perform certain actions,

Pickle implementation: (works for the dictionary before ui is drawn)

def save_game(game):
    with open(save_file_path, 'wb') as f:
        pickle.dump(game,f)```

RoomObjects, the elements within the dict being pickled
```py
 class RoomObject:
    def __init__(self, name:str | tuple["Hashable", str] | list[str | tuple["Hashable", str]]) -> None:
        self.name = name
        self.event = Event()
    def interact(self, button) -> None:
        pass
    def take_turn(self) -> None:
        self.event.emit(action=None)
    def handle_connecting_signals(self, dungeon):
        self.event.subscribe(dungeon.roomobject_event)

class Item(RoomObject):
    def interact(self, button) -> None:
        self.event.emit(action=classes.actions.TakeItemAction(self))

class Entity(RoomObject):
    def interact(self, button) -> None:
        pass

class Player(Entity):
    def take_turn(self) -> None:
        self.event.emit(action=classes.actions.PlayerInputAction())

class Passage(RoomObject):
    def __init__(self, name : str | tuple[Hashable, str] | list[str | tuple[Hashable, str]], destination_id : str):
        super().__init__(name)
        self.destination_id : str = destination_id
    
    def interact(self, button) -> None:
        self.event.emit(action=classes.actions.EnterPassageAction(self))```

An example "Action"
```py
class PlayerInputAction(InteractionAction):
    def execute(self, game_handler, actor):
        game_handler.room_center()```

The function connected to these signals

```py
def roomobject_event(self, action : classes.actions.InteractionAction):
        if action != None:
            action.execute(self, self.actor)```
#

The function in charge of generating the UI (contained in the Dungeon class, which is not being pickled)

def room_center(self):
        roomobjects = self.place.get_nonplayers()
        room_list = []
        room_list.append(urwid.Text(["Location: ", self.place.name]))
        room_list.append(urwid.Divider())
        for x in roomobjects:
            room_list.append(ActionButton(x.name, x.interact))
        room_list.append(ActionButton("Save and Quit (saving isn't working yet)", self.save_and_quit))
        center_widget : urwid.ListBox = urwid.ListBox(urwid.SimpleFocusListWalker(room_list))
        self.set_center_event.emit(new_center=center_widget)```

The function in charge of changing the UI
```py
def set_center(self, new_center : urwid.Widget):
        self.clear_center()
        self.center = new_center
        self.top.body = self.center```
#

I had thought that by using events, I would sufficiently isolate the data being pickled from the UI elements. Are the UI elements somehow being bundled in the pickled data?

mental widget
queen tiger
#
standard_map : dict = {
    "starting_room": Room("Starting room", [Player("Player Name"), Passage(("stone", u"Stone door"),"stone_room"), Passage(("iron",u"Iron door"),"iron_room"), Item(("wood", u"Wooden Sword"))]),
    "stone_room": Room(("stone", u"Stone Room"), [Passage(("wood", u"Wooden door"),"starting_room"), Passage(("iron",u"Iron door"),"iron_room"), Item(("magic", "Glyph-covered arm")), Item(("wood", u"Wooden Shield"))]),
    "iron_room": Room(("iron", u"Iron Room"), [Passage(("stone", u"Stone door"),"stone_room"), Passage(("wood", u"Wooden door"),"starting_room"), Item(("magic", u"Dragon Dreams"))]),
}```
#

This is the object being passed in for game

#

The pickling works properly when the project is first initialized, before any UI is generated, but fails later

#

I tried saving after the urwid loop was completed, and that also caused an error

mental widget
#

standard_map is game?

queen tiger
#

yeah

#

I'm calling py save_game(standard_map)

mental widget
#

What is Event?

queen tiger
#
class Event:
    def __init__(self):
        self.subscribers = []

    def subscribe(self, listener):
        self.subscribers.append(listener)

    def emit(self, *args, **kwargs):
        for subscriber in self.subscribers:
            subscriber(*args, **kwargs)```
just a basic class for signals
mental widget
#

Sorry, not sure.

queen tiger
#

I figured out the issue!

#

subscribers to the Events were being saved in the pickling

#

the solution, include this to clear out the subscribers from the event objects:

def __getstate__(self):
        state = self.__dict__.copy()
        # Remove the event reference before pickling.
        if 'event' in state:
            state['event'] = Event()
        return state```
signal sundialBOT
#
Python help channel closed for inactivity

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.