<div dir="ltr">Ahh, I didn't locate the documentation on the <a href="https://docs.python.org/3/library/constants.html#NotImplemented">NotImplemented constant</a> as I was checking 2.7's docs, so I wasn't aware of the layer of indirection and fallback behavior for the operator.   Sorry about that!<div><br></div><div>Based on that, the right call here is to adjust newstr to return that singleton where appropriate.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, May 7, 2019 at 9:23 PM Tim Peters <<a href="mailto:tim.peters@gmail.com">tim.peters@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">[Jordan Adler <<a href="mailto:jordan.m.adler@gmail.com" target="_blank">jordan.m.adler@gmail.com</a>>]<br>
> Through the course of work on the future polyfills that mimic the behavior<br>
> of Py3 builtins across versions of Python, we've discovered that the<br>
> equality check behavior of at least some builtin types do not match the<br>
> documented core data model.<br>
><br>
> Specifically, a comparison between a primitive (int, str, float were tested)<br>
> and an object of a different type always return False, instead of raising<br>
> a NotImplementedError.  Consider `1 == '1'` as a test case.<br>
><br>
> Should the data model be adjusted to declare that primitive types are<br>
> expected to fallback to False, or should the cpython primitive type's<br>
> __eq__ implementation fallback to raise NotImplementedError?<br>
<br>
Nope ;-)  This isn't a "data model" issue.  Look instead at the<br>
Standard Library manual's section on Built-In Types, under heading<br>
Comparisons:<br>
<br>
"""<br>
Objects of different types, except different numeric types, never<br>
compare equal. ...<br>
The <, <=, > and >= operators will raise a TypeError exception when<br>
comparing a complex number with another built-in numeric type, when<br>
the objects are of different types that cannot be compared, or in<br>
other cases where there is no defined ordering.<br>
"""<br>
<br>
It's not an object's responsibility to arrange for that.  It's done<br>
for them by default, and objects only need to supply their own rich<br>
comparison methods if they don't want the defaults.  For example, when<br>
comparing an int with another type, all the int rich comparison<br>
methods _do_ return NotImplemented:<br>
<br>
>>> f = 4<br>
>>> f.__eq__("abc")<br>
NotImplemented<br>
<br>
It's at a higher level that comparison logic says "OK, I gave both<br>
comparands a chance, and they both returned NotImplemented.  So one<br>
last chance (from object.c's do_richcompare())":<br>
<br>
    /* If neither object implements it, provide a sensible default<br>
       for == and !=, but raise an exception for ordering. */<br>
    switch (op) {<br>
    case Py_EQ:<br>
        res = (v == w) ? Py_True : Py_False;<br>
        break;<br>
    case Py_NE:<br>
        res = (v != w) ? Py_True : Py_False;<br>
        break;<br>
    default:<br>
        PyErr_Format(PyExc_TypeError,<br>
                     "'%s' not supported between instances of '%.100s'<br>
and '%.100s'",<br>
                     opstrings[op],<br>
                     v->ob_type->tp_name,<br>
                     w->ob_type->tp_name);<br>
        return NULL;<br>
    }<br>
<br>
Then the Py_EQ case of that delivers:<br>
<br>
>>> f == "abc"<br>
False<br>
<br>
and the Py_NE case:<br>
<br>
>>> f != "abc"<br>
True<br>
<br>
despite that (or because of that ;-) ):<br>
<br>
>>> f.__eq__("abc")<br>
NotImplemented<br>
>>> "abc".__eq__(f)<br>
NotImplemented<br>
<br>
Note that there's nothing special about builtin types here.  All types<br>
are treated alike.<br>
</blockquote></div>