Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests fail in 2038 #56

Closed
bmwiedemann opened this issue Jul 31, 2023 · 8 comments · Fixed by #62
Closed

tests fail in 2038 #56

bmwiedemann opened this issue Jul 31, 2023 · 8 comments · Fixed by #62
Labels

Comments

@bmwiedemann
Copy link

BUG/PROBLEM REPORT / FEATURE REQUEST

What I did:

osc co openSUSE:Factory/python-DateTime && cd $_
osc build --vm-type=kvm --noservice --clean --build-opt=--vm-custom-opt="-rtc base=2039-09-02T06:07:00" --alternative-project=home:bmwiedemann:reproducible openSUSE_Tumbleweed

What I expect to happen:

tests should keep working in the future (at least 16 years)

What actually happened:

similar to issue #41 in that there is a rounding error in DateTime-5.2 that can randomly break a test.

 =================================== FAILURES ===================================
 __________________________ DateTimeTests.test_pickle ___________________________
 
 self = <DateTime.tests.test_datetime.DateTimeTests testMethod=test_pickle>
 
     def test_pickle(self):
         dt = DateTime()
         data = pickle.dumps(dt, 1)
         new = pickle.loads(data)
         for key in DateTime.__slots__:
 >           self.assertEqual(getattr(dt, key), getattr(new, key))
 E           AssertionError: 2198556426235027 != 2198556426235026
 
 src/DateTime/tests/test_datetime.py:253: AssertionError
 =============================== warnings summary ===============================

What version of Python and Zope/Addons I am using:

openSUSE-Tumbleweed-20230729 python3.10

@bmwiedemann
Copy link
Author

still there in DateTime-5.4

@icemac
Copy link
Member

icemac commented Mar 19, 2024

@d-maurer Do have an idea what's going wrong here?

@icemac icemac added the bug label Mar 19, 2024
@d-maurer
Copy link
Contributor

d-maurer commented Mar 19, 2024 via email

@perrinjerome
Copy link
Contributor

For reference:

def __getstate__(self):
# We store a float of _micros, instead of the _micros long, as we most
# often don't have any sub-second resolution and can save those bytes
return (self._micros / 1000000.0,
getattr(self, '_timezone_naive', False),
self._tz)
def __setstate__(self, value):
if isinstance(value, tuple):
self._parse_args(value[0], value[2])
self._micros = long(value[0] * 1000000)
self._timezone_naive = value[1]
else:
for k, v in value.items():
if k in self.__slots__:
setattr(self, k, v)
# BBB: support for very old DateTime pickles
if '_micros' not in value:
self._micros = long(value['_t'] * 1000000)
if '_timezone_naive' not in value:
self._timezone_naive = False

this was set as a float to have shorter pickles, this was apparently wrong for this reason. In our case ( cc @fdiary ), we considered that it is wrong because of the precision loss and we are using a monkey patch to export the int in __getstate__ ( for this and other historical reasons ).

Unlike with protocol 1, a float is no longer serialized shorter pickle than an int with pickle protocol 3:

>>> pickletools.dis(pickle.dumps(2198556426.235026, 3))
    0: \x80 PROTO      3
    2: G    BINFLOAT   2198556426.235026
   11: .    STOP
highest protocol among opcodes = 2
>>> pickletools.dis(pickle.dumps(int(2198556426.235026 * 1000000.0), 3))
    0: \x80 PROTO      3
    2: \x8a LONG1      2198556426235026
   11: .    STOP
highest protocol among opcodes = 2

for the fix, I think we can consider exporting the int "as is" during __getstate__ and add more conditions during the __setstate__ to support the new format.

@d-maurer
Copy link
Contributor

d-maurer commented Mar 19, 2024 via email

@perrinjerome
Copy link
Contributor

I assume this int represents "microseconds since epoch`.

exactly:

# self._micros is the time since the epoch
# in long integer microseconds.
if microsecs is None:
microsecs = long(round(t * 1000000.0))
self._micros = microsecs

I submitted #62 with this change we discussed.

I tried to run osc myself but I had errors when trying to create an account, @bmwiedemann can you try easily from the linked PR ?

@bmwiedemann
Copy link
Author

looks good in a quick test.

perrinjerome added a commit that referenced this issue Mar 21, 2024
During pickle serialization, the `_micros` attribute was casted to float,
to save a little bit of disk space by making shorter pickles, this was
true when ZODB was using pickle protocol 1, but nowadays it uses protocol 3
and exporting as an int produces pickles of the same length as with a float.

Fixes #56

Co-authored-by: Dieter Maurer <d-maurer@users.noreply.github.com>
@icemac
Copy link
Member

icemac commented Mar 21, 2024

Fix released in https://pypi.org/project/DateTime/5.5/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants