On Mon, May 31, 2021 at 09:12:42PM -0400, David Mertz wrote:
> >
> > I think you are going against the design of the language here. With only
> >
> a handful of critical exceptional cases (None, True, False are the only
> > ones that come to mind), names can be rebound.
> >
> 
> The following genuinely surprised me.  I was trying to show something
> different in reply, but I think the actual behavior makes the point even
> more:
[...]
> This is *strange* behavior.  I don't expect every sequence of characters to
> round trip `eval(repr())`, but I kinda expect it to be mostly idempotent.

It's pretty standard behaviour for Python.

    >>> err = ValueError('something went wrong')
    >>> ValueError = list
    >>> eval(repr(err))
    ['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', ' ', 'w', 'e', 'n', 
    't', ' ', 'w', 'r', 'o', 'n', 'g']


The repr of an object tells you what the object thinks it is called; but 
`eval` evaluates the given source code according to whatever name 
bindings happen to exist at the time, which can shadow or monkey-patch 
the names that were used to generate the object in the first place.

    >>> eval(repr(NotImplemented))
    NotImplemented
    >>> NotImplemented = len
    >>> eval(repr(str.__lt__('', None)))
    <built-in function len>


The lesson here is not to rebind names if you don't want eval to use the 
rebound names :-)

Right now, `eval(repr(...))` works unless you have shadowed the name 
Ellipsis. If we make the proposed changed, it will call whatever 
arbitrary object was bound to Ellipsis, so the best you can hope for is 
a TypeError:

    >>> eval('Ellipsis (...)')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1, in <module>
    TypeError: 'ellipsis' object is not callable


Unless we make the ellipsis object `...` callable. But that doesn't help 
us if the name Ellipsis has been rebound. Then it will call the rebound 
object.

I have to emphasise that this is standard, normal behaviour, and I still 
don't know why we are disturbed by Ellipsis behaving like nearly 
everything else in Python. The only oddity is that unlike most things, 
Ellipsis also has a special symbol `...` that most objects don't have.


[...]
> Let's change the behavior of the Ellipsis object slightly to have either a
> .__call__() or .__getitem__() method that returns itself, no matter what
> argument is passed.

"Errors should never pass silently."

    class Ellipse(Shape):
        ...

    myshape = Ellipsis(mysquare)  # oops
    # much later on...
    myshape.draw()  # TypeError


Okay, it's a bit of a contrived set of circumstances, but it 
demonstrates the idea that functions should not ignore their arguments. 
Exceptions should, if possible, occur as close as possible to the actual 
error (the use of Ellipsis instead of Ellipse).

If the parameter to a function has no effect, it shouldn't be a 
parameter. If you pass an invalid argument to the (hypothetical) 
callable Ellipsis, it should raise TypeError immediately, not ignore the 
argument.

So we're trying to fix an issue of next to no practical importance, that 
doesn't really affect anyone, by building in more complexity and weird 
corner cases in the language. I don't think that's a good trade off. 
Shadowing and monkey-patching are advanced techniques, and we shouldn't 
demand the language protect newbies who accidentally shadow the built-in 
Ellipsis and then try to use eval.

Not every builtin needs a mollyguard to protect against misuse.



-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/QSURXGJCTK6NC2ZBJHLIGT7YHWVICBRE/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to