avdstaaij / gdpc

A python framework for generative design in Minecraft with the GDMC-HTTP mod
MIT License
22 stars 18 forks source link

Editor.__del__() sometimes crashes for some users #68

Open avdstaaij opened 1 year ago

avdstaaij commented 1 year ago

For some users, Editor.__del__() crashes when there are still blocks in the buffer (i.e. when it performs an automatic buffer flush) because of an inability to send HTTP requests while the Python interpreter is shutting down. This causes the last couple of blocks to not be placed.

Strangely, this issue seems to be quite rare. I cannot reproduce it myself. Further investigation is needed to find out what the precise cause is.

For anyone that experiences this bug: you can avoid it by calling Editor.flushBuffer() at the very end of your program.

avdstaaij commented 1 year ago

I'm not 100% sure, but it seems this issue occurs more often ~(or only?)~ for Windows users. Furthermore, besides calling Editor.flushBuffer, it can also be avoided by constructing the Editor instance inside a function instead of as a global, since Editor.__del__ will then be called when the function ends (though I believe this is not actually guaranteed).

2024-06-01 EDIT: Also confirmed on Linux.

avdstaaij commented 1 year ago

@Phobos97 has discovered that, on their (Windows) machine, the following causes a crash:

from gdpc import Editor, Block
from glm import ivec3

editor = Editor(buffering=True)
build_area = editor.getBuildArea()
editor.placeBlock(ivec3(-55, -60, 8), Block("stone"))

while the following runs without problems:

from gdpc import Editor, Block
from glm import ivec3

editor = Editor(buffering=True)
# build_area = editor.getBuildArea()
editor.placeBlock(ivec3(-55, -60, 8), Block("stone"))

The first code snippet crashed with the follwing traceback:

Exception ignored in: <function Editor.__del__ at 0x000002C247344820>
Traceback (most recent call last):
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\editor.py", line 89, in __del__
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\editor.py", line 532, in flushBuffer
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\editor.py", line 493, in flush
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\interface.py", line 151, in placeBlocks
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\interface.py", line 44, in _request
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\gdpc\utils.py", line 66, in withRetries
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\api.py", line 59, in request
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py", line 573, in request
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\sessions.py", line 481, in prepare_request
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\site-packages\requests\utils.py", line 235, in get_netrc_auth
  File "C:\Users\j-elm\AppData\Local\Programs\Python\Python39\lib\netrc.py", line 29, in __init__
LookupError: unknown encoding: ascii
avdstaaij commented 1 year ago

@Phobos97 and I did some more testing, and it turns out that the following snippets also cause a crash on their machine:

from gdpc import Editor, Block
from glm import ivec3

editor = Editor(buffering=True)

for i in range(1025):
    editor.placeBlock(ivec3(0, 0, i), Block("stone"))
from gdpc import Editor, Block
from glm import ivec3

editor = Editor(buffering=True)
version = editor.getMinecraftVersion()
editor.placeBlock(ivec3(-55, -60, 8), Block("stone"))

Therefore, it seems that the crash occurs when a buffer flush happens in Editor.__del__ and at least one other request has already been sent.

avdstaaij commented 4 months ago

I think the only real way to fix this is to make Editor work with the with statement, i.e.:

with editor = Editor():
    ...

Python's __del__ method is simply not reliable enough for this kind of cleanup: https://stackoverflow.com/q/34983137 https://stackoverflow.com/q/40536821 https://docs.python.org/3/reference/datamodel.html#object.__del__