currently lots of code manages contexts incorrectly by doing: ``` @dataclasses.dataclass class WrapCmgr: _cmgr: ContextManager[T] def __enter__(self) -> Wrapped[T]: return wrap(_cmgr.__enter__()) def __exit__(self, \, t: Type[BaseException] | None, v: BaseException, tb: types.TracebackType) -> bool: return _cmgr.__exit__(t, v, tb) ``` since https://bugs.python.org/issue44471 using `with WrapCmgr(cmgr()):` incorrectly raises an AttributeError rather than a TypeError and https://www.python.org/dev/peps/pep-0343/ still includes this bug, raising an AttributeError ``` mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) ``` contextlib.ExitStack also had this bug https://bugs.python.org/issue12022 given that it's hard to implement this correctly I think there should be a builtins.enter and builtins.aenter for use like this: ``` @dataclasses.dataclass class WrapCmgr: _cmgr: ContextManager[T] def __enter__(self) -> Wrapped[T]: exit, value = enter(self._cmgr) self.__exit = exit return wrap(value) def __exit__(self, \, t: Type[BaseException] | None, v: BaseException, tb: types.TracebackType) -> bool: return self.__exit(t, v, tb) ```
13.07.21 18:59, Thomas Grainger пише:
given that it's hard to implement this correctly I think there should be a builtins.enter and builtins.aenter for use like this:
``` @dataclasses.dataclass class WrapCmgr: _cmgr: ContextManager[T]
def __enter__(self) -> Wrapped[T]: exit, value = enter(self._cmgr) self.__exit = exit return wrap(value)
def __exit__(self, \, t: Type[BaseException] | None, v: BaseException, tb: types.TracebackType) -> bool: return self.__exit(t, v, tb) ```
I considered similar idea when worked on issue12022 and issue44471. The only difference is that I added the helper in the operator module, and my operator.enter() returned the value and the callback in the different order. def enter(cm): # We look up the special methods on the type to match the with # statement. cls = type(cm) try: enter = cls.__enter__ exit = cls.__exit__ except AttributeError: raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does " f"not support the context manager protocol") from None callback = _MethodType(exit, cm) return enter(cm), callback I still investigate possible consequences and alternatives. Since we need to save the callback as an object attribute in any case, one of alternatives is using ExitStack instead of introducing new API. It is more heavyweight, but if implemented in C the difference can be reduced. Other alternative is to expose _PyObject_LookupSpecial() at Python level. It would be useful not only for __enter__ and __exit__, but for other special methods.
I used the order I did because it's idiomatic to return the value the user needs followed by the value the user wants. On Tue, 13 Jul 2021, 20:24 Serhiy Storchaka, <storchaka@gmail.com> wrote:
13.07.21 18:59, Thomas Grainger пише:
given that it's hard to implement this correctly I think there should be a builtins.enter and builtins.aenter for use like this:
``` @dataclasses.dataclass class WrapCmgr: _cmgr: ContextManager[T]
def __enter__(self) -> Wrapped[T]: exit, value = enter(self._cmgr) self.__exit = exit return wrap(value)
def __exit__(self, \, t: Type[BaseException] | None, v: BaseException, tb: types.TracebackType) -> bool: return self.__exit(t, v, tb) ```
I considered similar idea when worked on issue12022 and issue44471. The only difference is that I added the helper in the operator module, and my operator.enter() returned the value and the callback in the different order.
def enter(cm): # We look up the special methods on the type to match the with # statement. cls = type(cm) try: enter = cls.__enter__ exit = cls.__exit__ except AttributeError: raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does " f"not support the context manager protocol") from None callback = _MethodType(exit, cm) return enter(cm), callback
I still investigate possible consequences and alternatives.
Since we need to save the callback as an object attribute in any case, one of alternatives is using ExitStack instead of introducing new API. It is more heavyweight, but if implemented in C the difference can be reduced.
Other alternative is to expose _PyObject_LookupSpecial() at Python level. It would be useful not only for __enter__ and __exit__, but for other special methods.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/FAQAGF... Code of Conduct: http://python.org/psf/codeofconduct/
Like the nodeback interface, it's (err, val) On Tue, 13 Jul 2021, 21:31 Ethan Furman, <ethan@stoneleaf.us> wrote:
On 7/13/21 12:43 PM, Thomas Grainger wrote:
I used the order I did because it's idiomatic to return the value the user needs followed by the value the user wants.
citation?
-- ~Ethan~ _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/SBUNQO... Code of Conduct: http://python.org/psf/codeofconduct/
On 7/13/21 2:20 PM, Thomas Grainger wrote:
On Tue, 13 Jul 2021, 21:31 Ethan Furman, wrote:
On 7/13/21 12:43 PM, Thomas Grainger wrote:
I used the order I did because it's idiomatic to return the value the user needs followed by the value the user wants.
citation?
Like the nodeback interface, it's (err, val)
Isn't that javascript? Javascript idioms are not (necessarily) Python idioms. -- ~Ethan~
Ethan Furman writes:
Isn't that javascript? Javascript idioms are not (necessarily) Python idioms.
True, but unless there is (preferably) a Python idiom or one from another language we borrow from at least somewhat frequently, why not adopt the Javascript idiom? Note: I don't think you were arguing *against* this idiom, just querying the relevance. I did question the OP's "value you need, value you want" characterization. It's not obvious to me you can decide that for the caller.
Another example, is a cash point (ATM) won't give you your money until you take your card On Fri, 16 Jul 2021, 09:28 Stephen J. Turnbull, < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Ethan Furman writes:
Isn't that javascript? Javascript idioms are not (necessarily) Python idioms.
True, but unless there is (preferably) a Python idiom or one from another language we borrow from at least somewhat frequently, why not adopt the Javascript idiom?
Note: I don't think you were arguing *against* this idiom, just querying the relevance.
I did question the OP's "value you need, value you want" characterization. It's not obvious to me you can decide that for the caller.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/WO74OJ... Code of Conduct: http://python.org/psf/codeofconduct/
Thomas Grainger writes:
Another example, is a cash point (ATM) won't give you your money until you take your card
That ATM is effective in enforcing the desired behavior. In Python you would usually use an exception to force handling. Returning status codes, or couples of status codes and values, isn't nearly as effective. And a (status, value) couple does nothing to encourage checking the code over (value, status). Steve
On 2021-07-16 12:44, Stephen J. Turnbull wrote:
Thomas Grainger writes:
Another example, is a cash point (ATM) won't give you your money until you take your card
That ATM is effective in enforcing the desired behavior. In Python you would usually use an exception to force handling. Returning status codes, or couples of status codes and values, isn't nearly as effective.
And a (status, value) couple does nothing to encourage checking the code over (value, status).
To me, it makes more sense to return (status, value) than (value, status) because it's clearer to say "that didn't work, so you can just ignore the value" than "here's a value, but it didn't work, so just ignore the value even though I mentioned it first".
Right but it's not a status code - it's a callback that you *must* call On Fri, 16 Jul 2021, 17:17 MRAB, <python@mrabarnett.plus.com> wrote:
On 2021-07-16 12:44, Stephen J. Turnbull wrote:
Thomas Grainger writes:
Another example, is a cash point (ATM) won't give you your money until you take your card
That ATM is effective in enforcing the desired behavior. In Python you would usually use an exception to force handling. Returning status codes, or couples of status codes and values, isn't nearly as effective.
And a (status, value) couple does nothing to encourage checking the code over (value, status).
To me, it makes more sense to return (status, value) than (value, status) because it's clearer to say "that didn't work, so you can just ignore the value" than "here's a value, but it didn't work, so just ignore the value even though I mentioned it first". _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/MUAGZX... Code of Conduct: http://python.org/psf/codeofconduct/
Thomas Grainger writes:
Right but it's not a status code - it's a callback that you *must* call
OK. Thing is, Serhiy was just saying "hey I did almost exactly that (but minor technical detail)". In your idiomatic usage, 'exit' is almost invisible outside of the wrapped context manager. I don't see why anybody would care what order it's returned in Python (there might be some argument in Lisp where multiple values return requires a special API, but the primary value can be accessed as an ordinary return value). I guess instead of "return first what's needed, then what's wanted", you could define it "return first the items needed elsewhere in that API, then items you don't know what the user plans to do with them." That would be a rule people could follow (and I certainly would not have replied at all :-).
also https://www.python.org/dev/peps/pep-0343/ probably needs updating - but I'm not sure exactly what the code is
participants (5)
-
Ethan Furman
-
MRAB
-
Serhiy Storchaka
-
Stephen J. Turnbull
-
Thomas Grainger