ariebovenberg / whenever

⏰ Modern datetime library for Python
https://whenever.rtfd.io
MIT License
904 stars 15 forks source link

Windows access violation with Instant.from_utc() #167

Closed ArneBachmannDLR closed 2 months ago

ArneBachmannDLR commented 2 months ago

Trying to call Instant twice in a row hard-crashes the interpreter. I created a fresh conda environment and got this result:

(adb) D:\forks\IT\assetdb>python
Python 3.11.9 | packaged by conda-forge | (main, Apr 19 2024, 18:27:10) [MSC v.1938 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

>>> from whenever import Instant
>>> Instant.from_utc(1970, 1, 1)
Instant(1970-01-01 00:00:00Z)
>>> Instant.from_utc(1970, 1, 1)
Windows fatal exception: access violation

Current thread 0x0000527c (most recent call first):
  File "<stdin>", line 1 in <module>

I was initially assuming that my date computation was before 1.1.1970 leading to a negative unix timestamp + crash, but in this case there must be something else.

ariebovenberg commented 2 months ago

Thanks for posting! Looks like it's something else indeed. I'll have a look

ariebovenberg commented 2 months ago

It shouldn't have anything to do with the system time settings BTW, from_utc is just a simple object instantiation. Looks like the problem may be incorrect object deallocation, which can be tricky to reproduce.

In any case I wasn't so lucky to be able to reproduce, even on a matching conda/python/windows environment:

 PS C:\Users\acbov> python
Python 3.11.9 | packaged by Anaconda, Inc. | (main, Apr 19 2024, 16:40:41) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from whenever import Instant
>>> Instant.from_utc(1970, 1, 1)
Instant(1970-01-01 00:00:00Z)
>>> Instant.from_utc(1970, 1, 1)

Do you get the same problem with other values (e.g. Instant.from_utc(1980, 1, 1))? Or other classes (e.g. Time(0))?

ArneBachmannDLR commented 2 months ago

So weird...

>>> from whenever import Time
>>> Time(0)
Time(00:00:00)
>>> Time(0)
Windows fatal exception: access violation

>>> Instant.from_utc(1980,2,3)
Instant(1980-02-03 00:00:00Z)
>>> Instant.from_utc(1980,2,4)
Windows fatal exception: access violation
ariebovenberg commented 2 months ago

Hmmm. What if you do:

>>> Time(0)  # don't assign this to a variable!
>>> object()  # clears the reference to the previous object

That at least narrows the problem down to deallocation

ArneBachmannDLR commented 2 months ago
>>> Time(0)
Time(00:00:00)
>>> object()
Windows fatal exception: access violation
ariebovenberg commented 2 months ago

Alright, the issue is at least pinpointed. It appears on some windows installation, the deallocation routine fails. This is a bit surprising, since it's basically copied from established examples.

The real tricky bit is debugging if I can't reproduce...

One sanity check you could do: does using any other rust extension work? Can you try fastuuid?

>>> fastuuid.uuid4()
>>> object()
ariebovenberg commented 2 months ago

Ok I have an idea. Can you try my attempt at a fix (pip install whenever==0.6.8rc1)? It should be out once this workflow completes

ArneBachmannDLR commented 2 months ago

Seems to work!

>>> fastuuid.uuid4()
UUID('a66b265a-8901-4e8c-a72e-9ec0a08a66bd')
>>> object()
<object object at 0x000001FE46156920>

... upgrade whenever...
>>> from whenever import Time
>>> Time(0)
Time(00:00:00)
>>> Time(0)
Time(00:00:00)
>>> Time(0)
Time(00:00:00)
>>> from whenever import Instant
>>> Instant.from_utc(1970,1,1)
Instant(1970-01-01 00:00:00Z)
>>> Instant.from_utc(1970,1,1)
Instant(1970-01-01 00:00:00Z)
ariebovenberg commented 2 months ago

Excellent. A proper release (0.6.8) should be out once this workflow finishes.

ArneBachmannDLR commented 2 months ago

That's amazing! Was already able to continue the refactoring to the latest version which doesn't use _AwareDateTime anymore and replaced it with Instant

ariebovenberg commented 2 months ago

Happy to hear it. Thanks for taking the time to post 👍