equinor / dlisio

Python library for working with the well log formats Digital Log Interchange Standard (DLIS V1) and Log Information Standard (LIS79)
https://dlisio.readthedocs.io/en/latest/
Other
121 stars 39 forks source link

frame.curves() causes a kernel restart #357

Closed andymcdgeo closed 3 years ago

andymcdgeo commented 3 years ago

Hi,

I have been having issues trying to access the .curves() method of a frame. When I try to use this, my Jupyter Notebook kernel crashes and restarts. This occurs on both Windows 10 and MacOS

MacOS: Python Version: 3.8, DLISIO 0.3.1 Windows: Python 3.6.5, DLISIO 0.3.2

Sample code:

from dlisio import dlis

with dlis.load('Data/D15_05X_inline_xmac_results_dls.dlis') as (f, *tail):
    pass

frame1 = f.object('FRAME','0.076200B0')
frame2 = f.object('FRAME','0.152400B1')
print(frame1)
print(frame2)

f1curves = frame1.curves()

This results in the following error: Kernel Restarting The kernel for Untitled3.ipynb appears to have died. It will restart automatically.

I have uploaded the notebook and data to the following location for testing: https://github.com/andymcdgeo/dlisio_issue

ErlendHaa commented 3 years ago

Hi,

Thanks for the report and the notebook with a reproducible example!

The problem you are facing is that frame.curves() tries to read from a closed file. This is caused by incorrect use of Python's with-statement. The filehandle that dlisio.load opens are closed automatically when exiting the scope of the with-statements [1]. You therefore have to do all your calls in the scope of the with-statement:

with dlis.load('Data/D15_05X_inline_xmac_results_dls.dlis') as (f, *tail):
    frame1 = f.object('FRAME','0.076200B0')
    f1curves = frame1.curves()

But I myself usually drop with altogether when working with a single file in a notebook, as it works poorly with multiple cells and leaking file handles isn't a big issue:

f, *tail =  dlis.load('Data/D15_05X_inline_xmac_results_dls.dlis')
frame1 = f.object('FRAME','0.076200B0')
f1curves = frame1.curves()

f.close()                      # Manually close the filehandle when done with it
_ = [x.close() for x in tail]  # Each logical file has it's own unique filehandle, so make sure to close them all 

I realise it might be a bit confusing as to why f.object does not fail when called on a closed file. This due to internal caching of metadata structures, meaning that f.object does not need to read anything from disk. But you should think of any operation on a closed file as undefined behaviour.

Hope this clarifies things

[1] https://www.geeksforgeeks.org/with-statement-in-python/

andymcdgeo commented 3 years ago

Thanks very much @ErlendHaa. I had gone with the with statement to allow the file to close afterwards rather than using f.close() and incorrectly thought that the file was loaded into memory. I will update my code for this.

Would it be worth adding f, *tail = dlis.load('filename.dlis') into the documentation under Opening Files of the User Guide section as an alternative way of opening the file?

ErlendHaa commented 3 years ago

dlis.load and lis.load are both designed to work identically to Python's own open. (except for the extra support of automatic unpacking of the return value). I read through our documentation and this might not come off as clearly as it should. Might want clarify the docs.