<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p>Brett,<br>
<br>
Thank you for bringing this up, but I think you <i>may</i> have
misunderstood my position - though maybe you understood the thrust
and wanted to clarify for people coming in halfway, which I
applaud.</p>
<p>I proposed this change <i>knowing</i> that it was a breaking
change - it's why I brought it to the attention of datetime-SIG
and now python-dev - and I believe that there are several factors
that lead this to being a smaller compatibility problem than it
seems.</p>
<p>One such factor is the fact that <i>many</i> other features of
`datetime`, including the implementation of `datetime.now()` are <i>already
broken</i> in the current implementation for anyone who would be
broken by this particular aspect of the semantic change. That is
not saying that it's impossible that there is code out there that
will break if this change goes through, it's just saying that the
scope of the breakage is necessarily very limited.</p>
<p>The reason I brought up the bug tracker is because between Python
3.6 and Python 3.7, we in fact made a similar breaking change to
the one I'm proposing here without thinking that anyone might be
relying on the fact that they could do something like:<br>
<br>
class D(datetime.datetime):<br>
def __new__(cls):<br>
return cls.now()<br>
<br>
My point was that there have been no bug reports about the <i>existing
change</i> that Guido was bringing up (his example itself does
not work on Python 3.7!), which leads me to believe that few if
any people are relying on the fact that it is possible to define a
datetime subclass with a different default constructor.</p>
<p>As I mentioned, it is likely possible to have a transition period
where this would still work even if the subclassers have not
created their own __add__ method.<br>
<br>
There is no way to create a similar deprecation/transition period
for people relying on the fact that `type(datetime_obj +
timedelta_obj) == datetime.datetime`, but I think this is honestly
a sufficiently minor breakage that the good outweighs the harm. I
will note that we have already made several such changes with
respect to alternate constructors even though technically someone
could have been relying on the fact that
`MyDateTime(*args).replace(month=3)` returns a `datetime` object.</p>
<p>This is not to say that we should lightly make the change (hence
my canvassing for opinions), it is just that there is a good
amount of evidence that, practically speaking, no one is relying
on this, and in fact it is likely that people are writing code
that assumes that adding `timedelta` to a datetime subclass
returns the original subclass, either directly or indirectly - I
think we're likely to fix more people than we break if we make
this change.<br>
<br>
Best,<br>
Paul<br>
</p>
<p><br>
</p>
<div class="moz-cite-prefix">On 1/6/19 3:24 PM, Brett Cannon wrote:<br>
</div>
<blockquote type="cite"
cite="mid:CAP1=2W7RnpW7HgpuDQPzZzfrMMzPXAGj8G91xQyRMU4qLV1btg@mail.gmail.com">
<div dir="ltr">
<div dir="ltr"><br>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Sun, 6 Jan 2019 at 11:00, Paul Ganssle <<a
href="mailto:paul@ganssle.io" moz-do-not-send="true">paul@ganssle.io</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote">
<div>
<p>I did address this in the original post - the
assumption that the subclass constructor will have the
same arguments as the base constructor is baked into
many alternate constructors of datetime. I acknowledge
that this is a breaking change, but it is a small one -
anyone creating such a subclass that <i>cannot</i>
handled the class being created this way would be broken
in myriad ways.<br>
<br>
We have also in recent years changed several alternate
constructors (including `replace`) to retain the
original subclass, which by your same standard would be
a breaking change. I believe there have been no
complaints. In fact, between Python 3.6 and 3.7, the
very example you showed broke:<br>
<br>
Python 3.6.6:<br>
<br>
>>> class D(datetime.datetime):<br>
... def __new__(cls):<br>
... return cls.now()<br>
... <br>
>>> D()<br>
D(2019, 1, 6, 13, 49, 38, 842033)<br>
<br>
Python 3.7.2:<br>
<br>
>>> class D(datetime.datetime):<br>
... def __new__(cls):<br>
... return cls.now()<br>
... <br>
>>> D()<br>
Traceback (most recent call last):<br>
File "<stdin>", line 1, in <module><br>
File "<stdin>", line 3, in __new__<br>
TypeError: __new__() takes 1 positional argument but 9
were given</p>
<p><br>
We haven't seen any bug reports about this sort of
thing; what we <i>have</i> been getting is bug reports
that subclassing datetime doesn't retain the subclass in
various ways (because people <i>are</i> using datetime
subclasses).</p>
</div>
</blockquote>
<div><br>
</div>
<div>To help set expectations, the current semantics are not a
bug and so the proposal isn't fixing a bug but proposing a
change in semantics.<br>
</div>
<div> </div>
<blockquote class="gmail_quote">
<div>
<p> This is likely to cause very little in the way of
problems, but it will improve convenience for people
making datetime subclasses and almost certainly
performance for people using them (e.g. pendulum and
arrow, which now need to take a slow pure python route
in many situations to work around this problem).</p>
<p>If we're <i>really</i> concerned with this backward
compatibility breaking, </p>
</div>
</blockquote>
<div><br>
</div>
<div>We very much do care. Because this isn't a bug but a
voluntary semantic change you're proposing to change we
can't blindly break people who are relying on the current
semantics. We need to have a justification for those people
as to why we have decided to change the semantics now after
all of these years as well as provide an upgrade path.</div>
<div><br>
</div>
<div>-Brett<br>
</div>
<div> </div>
<blockquote class="gmail_quote">
<div>
<p>we could do the equivalent of:<br>
<tt><br>
</tt><tt>try:</tt><tt><br>
</tt><tt> return new_behavior(...)</tt><tt><br>
</tt><tt>except TypeError:</tt><tt><br>
</tt><tt> warnings.warn("The semantics of timedelta
addition have "</tt><tt><br>
</tt><tt> "changed in a way that raises
an error in "<br>
"this subclass. Please implement
__add__ "<br>
"if you need the old behavior.",
DeprecationWarning)</tt> <br>
</p>
</div>
</blockquote>
<blockquote class="gmail_quote">
<div>
<p><tt> <br>
</tt>Then after a suitable notice period drop the
warning and turn it to a hard error.<br>
</p>
<p>Best,</p>
<p>Paul<br>
</p>
<div class="gmail-m_8458662023297668833moz-cite-prefix">On
1/6/19 1:43 PM, Guido van Rossum wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div>I don't think datetime and builtins like
int necessarily need to be aligned. But I do
see a problem -- the __new__ and __init__
methods defined in the subclass (if any)
should allow for being called with the same
signature as the base datetime class.
Currently you can have a subclass of datetime
whose __new__ has no arguments (or, more
realistically, interprets its arguments
differently). Instances of such a class can
still be added to a timedelta. The proposal
would cause this to break (since such an
addition has to create a new instance, which
calls __new__ and __init__). Since this is a
backwards incompatibility, I don't see how it
can be done -- and I also don't see many use
cases, so I think it's not worth pursuing
further.<br>
</div>
<div><br>
</div>
<div>Note that the same problem already happens
with the .fromordinal() class method, though
it doesn't happen with .fromdatetime() or
.now():</div>
<div><br>
</div>
<div>>>> class D(datetime.datetime):<br>
... def __new__(cls): return cls.now()<br>
... <br>
>>> D()<br>
D(2019, 1, 6, 10, 33, 37, 161606)<br>
>>> D.fromordinal(100)<br>
Traceback (most recent call last):<br>
File "<stdin>", line 1, in
<module><br>
TypeError: __new__() takes 1 positional
argument but 4 were given<br>
>>> D.fromtimestamp(123456789)<br>
D(1973, 11, 29, 13, 33, 9)<br>
>>> <br>
</div>
</div>
</div>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Sun, Jan 6, 2019 at 9:05 AM Paul
Ganssle <<a href="mailto:paul@ganssle.io"
target="_blank" moz-do-not-send="true">paul@ganssle.io</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote">
<div>
<p>I can think of many reasons why datetime is
different from builtins, though to be honest I'm
not sure that consistency for its own sake is
really a strong argument for keeping a
counter-intuitive behavior - and to be honest
I'm open to the idea that <i>all</i> arithmetic
types <i>should</i> have some form of this
change.<br>
</p>
<p>That said, I would say that the biggest
difference between datetime and builtins (other
than the fact that datetime is <i>not</i> a
builtin, and as such doesn't necessarily need to
be categorized in this group), is that unlike
almost all other arithmetic types, <i>datetime</i>
has a special, dedicated type for describing
differences in datetimes. Using your example of
a float subclass, consider that without the
behavior of "addition of floats returns floats",
it would be hard to predict what would happen in
this situation:<br>
<br>
>>> F(1.2) + 3.4<br>
<br>
Would that always return a float, even though
F(1.2) + F(3.4) returns an F? Would that return
an F because F is the left-hand operand? Would
it return a float because float is the
right-hand operand? Would you walk the MROs and
find the lowest type in common between the
operands and return that? It's not entirely
clear which subtype predominates. With datetime,
you have:<br>
<br>
datetime - datetime -> timedelta<br>
datetime ± timedelta -> datetime<br>
timedelta ± timedelta -> timedelta<br>
<br>
There's no operation between two datetime
objects that would return a datetime object, so
it's always clear: operations between datetime
subclasses return timedelta, operations between
a datetime object and a timedelta return the
subclass of the datetime that it was added to or
subtracted from.</p>
<p>Of course, the real way to resolve whether
datetime should be different from
int/float/string/etc is to look at why this
choice was actually made for those types in the
first place, and decide whether datetime is like
them <i>in this respect</i>. The heterogeneous
operations problem may be a reasonable
justification for leaving the other builtins
alone but changing datetime, but if someone
knows of other fundamental reasons why the
decision to have arithmetic operations always
create the base class was chosen, please let me
know.<br>
<br>
Best,<br>
Paul<br>
</p>
<div
class="gmail-m_8458662023297668833gmail-m_-6009346472102158879moz-cite-prefix">On
1/5/19 3:55 AM, Alexander Belopolsky wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr">
<div dir="ltr"><br>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr">On Wed, Jan 2, 2019 at
10:18 PM Paul Ganssle <<a
href="mailto:paul@ganssle.io"
target="_blank"
moz-do-not-send="true">paul@ganssle.io</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote">
<div>
<p>.. the original objection was
that this implementation assumes
that the datetime subclass has a
constructor with the same (or a
sufficiently similar) signature as
datetime.</p>
</div>
</blockquote>
<div>While this was used as a possible
rationale for the way standard types
behave, the main objection to changing
datetime classes is that it will make
them behave differently from
builtins. For example:</div>
<div>
<div><br>
</div>
<div>>>> class F(float):</div>
<div>... pass</div>
<div>...</div>
</div>
<div>
<div>>>>
type(F.fromhex('AA'))</div>
<div><class '__main__.F'></div>
<div>>>> type(F(1) + F(2))</div>
<div><class 'float'></div>
</div>
<div><br>
</div>
<blockquote class="gmail_quote">
<div>
<p> This may be a legitimate gripe,
but unfortunately that ship has
sailed long ago. All of datetime's
alternate constructors make this
assumption. Any subclass that does
not meet this requirement must
have worked around it long ago (or
they don't care about alternate
constructors).</p>
</div>
</blockquote>
<div><br>
</div>
<div>This is right, but the same
argument is equally applicable to int,
float, etc. subclasses. If you want
to limit your change to datetime types
you should explain what makes these
types special. </div>
</div>
</div>
</div>
</div>
</blockquote>
</div>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org"
target="_blank" moz-do-not-send="true">Python-Dev@python.org</a><br>
<a
href="https://mail.python.org/mailman/listinfo/python-dev"
rel="noreferrer" target="_blank"
moz-do-not-send="true">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a
href="https://mail.python.org/mailman/options/python-dev/guido%40python.org"
rel="noreferrer" target="_blank"
moz-do-not-send="true">https://mail.python.org/mailman/options/python-dev/guido%40python.org</a><br>
</blockquote>
</div>
<br>
<br>
-- <br>
<div dir="ltr"
class="gmail-m_8458662023297668833gmail_signature">--Guido
van Rossum (<a href="http://python.org/~guido"
target="_blank" moz-do-not-send="true">python.org/~guido</a>)</div>
</blockquote>
</div>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" target="_blank"
moz-do-not-send="true">Python-Dev@python.org</a><br>
<a
href="https://mail.python.org/mailman/listinfo/python-dev"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a
href="https://mail.python.org/mailman/options/python-dev/brett%40python.org"
rel="noreferrer" target="_blank" moz-do-not-send="true">https://mail.python.org/mailman/options/python-dev/brett%40python.org</a><br>
</blockquote>
</div>
</div>
</blockquote>
</body>
</html>