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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/QSURXGJCTK6NC2ZBJHLIGT7YHWVICBRE/
Code of Conduct: http://python.org/psf/codeofconduct/