Skip to content

Conversation

@lelit
Copy link
Contributor

@lelit lelit commented Jun 12, 2017

See issue #150 for details.

lelit added 13 commits June 9, 2017 17:21
This is a POC, targeting issue MagicStack#150: PostgreSQL interval data type and Python timedelta have a similar goal, but are subtly different because PG date arithmetic is much more versatile. So, instead of trying to coerce an interval to a timedelta, the new class aims to carry the same bits of information, and may implement a similar semantic, if needed.
Implement most of the PostgreSQL interval arithmetic, making the new class a credible replacement of the Python timedelta.
This seems more natural and allows Sphinx to extract its documentation.
@1st1 1st1 requested a review from elprans June 30, 2017 19:27

return result + delta

elif isinstance(self, datetime.date):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this branch be ever triggered? self should always be an instance of Interval, doesn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return ((self.months, self.days, self.time)
>=
(other.months, other.days, other.time))
return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return NotImplemented here? Can we add a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, from my understanding and tests of the Cython richcmp(), if other is not an instance of Interval then it should return False. I will try to find again the examples that lead me there.

@1st1
Copy link
Member

1st1 commented Jul 4, 2017

Should we add an Interval.as_timedelta() method? @elprans?

readonly int64_t time

def __init__(self, int32_t months, int32_t days, int64_t time=0,
int64_t hours=0, int64_t minutes=0, int64_t seconds=0, int64_t microseconds=0):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All code lines must be shorter than 79 chars.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will rectify that.

self.days - other.days,
self.time - other.time)

elif isinstance(self, datetime.date):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'm not sure how this branch can ever be triggered.

@1st1
Copy link
Member

1st1 commented Jul 4, 2017

This change can also break backwards compatibility. Any code that expected to work with timedelta, will receive an asyncpg.Interval with this PR landed.

@lelit
Copy link
Contributor Author

lelit commented Jul 4, 2017

Should we add an Interval.as_timedelta() method?

I thought about that, but they are really not compatible, at least when they contain months: there's no obvious way to represent a PG interval with a Python timedelta (... otherwise we could avoid the new Interval class all together).

@lelit
Copy link
Contributor Author

lelit commented Jul 4, 2017

This change can also break backwards compatibility. Any code that expected to work with timedelta, will receive an asyncpg.Interval with this PR landed.

Yeah, I know. But I still think that is less surprising having a different instance, than a wrong value as the original issue demonstrate, isn't it?

@lelit
Copy link
Contributor Author

lelit commented Jul 4, 2017

Thank you for the review!

@elprans
Copy link
Member

elprans commented Jul 4, 2017

After some consideration with @1st1 we decided that merging a custom Interval type solution wouldn't be the best fix maintenance-wise. Time handling is a complex issue, and finding solutions that would work for all requirements is hard. Also a number of third-party libraries that handle relative timedeltas exist (you mentioned MonthDelta, there's also dateutil and its relativedelta). Thus, we've decided to handle this as follows:

  1. Fix the interval codec to behave like the psycopg2 version, i.e treat 12-month increments as 365-day spans, so that your original example from Conversion problem between interval and timedelta #150 works the same way under both asyncpg and psycopg2. See PR Make interval decoding logic match that of psycopg2. #165.
  2. Extend Connection.set_type_codec() to allow "structured" codecs that would accept and return tuples. For example, a custom interval codec would receive a (months, days, seconds, microseconds) tuple. You would then be able to plug your Interval class easily.
@elprans
Copy link
Member

elprans commented Jul 4, 2017

Thanks a lot for finding and taking the time to tackle the issue, @lelit!

@lelit
Copy link
Contributor Author

lelit commented Jul 4, 2017

Ok, up to you. I will then try to implement this as a third party module.

@elprans elprans closed this Jul 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants