PEP 559 - built-in noop()

I couldn’t resist one more PEP from the Core sprint. I won’t reveal where or how this one came to me. -Barry PEP: 559 Title: Built-in noop() Author: Barry Warsaw <barry@python.org> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 2017-09-08 Python-Version: 3.7 Post-History: 2017-09-09 Abstract ======== This PEP proposes adding a new built-in function called ``noop()`` which does nothing but return ``None``. Rationale ========= It is trivial to implement a no-op function in Python. It's so easy in fact that many people do it many times over and over again. It would be useful in many cases to have a common built-in function that does nothing. One use case would be for PEP 553, where you could set the breakpoint environment variable to the following in order to effectively disable it:: $ setenv PYTHONBREAKPOINT=noop Implementation ============== The Python equivalent of the ``noop()`` function is exactly:: def noop(*args, **kws): return None The C built-in implementation is available as a pull request. Rejected alternatives ===================== ``noop()`` returns something ---------------------------- YAGNI. This is rejected because it complicates the semantics. For example, if you always return both ``*args`` and ``**kws``, what do you return when none of those are given? Returning a tuple of ``((), {})`` is kind of ugly, but provides consistency. But you might also want to just return ``None`` since that's also conceptually what the function was passed. Or, what if you pass in exactly one positional argument, e.g. ``noop(7)``. Do you return ``7`` or ``((7,), {})``? And so on. The author claims that you won't ever need the return value of ``noop()`` so it will always return ``None``. Coghlin's Dialogs (edited for formatting): My counterargument to this would be ``map(noop, iterable)``, ``sorted(iterable, key=noop)``, etc. (``filter``, ``max``, and ``min`` all accept callables that accept a single argument, as do many of the itertools operations). Making ``noop()`` a useful default function in those cases just needs the definition to be:: def noop(*args, **kwds): return args[0] if args else None The counterargument to the counterargument is that using ``None`` as the default in all these cases is going to be faster, since it lets the algorithm skip the callback entirely, rather than calling it and having it do nothing useful. Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 coding: utf-8 End:

I don't think the rationale justifies an entire builtin. You could just use "PYTHONBREAKPOINT=int" to disable, or support "PYTHONBREAKPOINT=0" as I think someone else suggested. I personally can't remember the last time I needed a noop() function. I've more often needed an identity() function, and even that has been proposed several times but shot down, mainly because different people have different needs. (Often "None" provided for function arguments means identity anyway, like for sorted() and filter().) If anything, I think it should be functools.noop() and PYTHONBREAKPOINT=functools.noop I think we need more justification and examples of where this would be helpful to justify a new builtin. -Ben On Sat, Sep 9, 2017 at 2:46 PM, Barry Warsaw <barry@python.org> wrote:

I always wanted this feature (no kidding). Would it be possible to add support for the context manager? with noop(): ... Maybe noop can be an instance of: class Noop: def __enter__(self, *args, **kw): return self def __exit__(self, *args): pass def __call__(self, *args, **kw): return self Victor Le 9 sept. 2017 11:48 AM, "Barry Warsaw" <barry@python.org> a écrit :

I was able to find a real keyboard, so here is a more complete code: --- class Noop: def __call__(self, *args, **kw): return self def __enter__(self, *args, **kw): return self def __exit__(self, *args): return def __repr__(self): return 'nope' --- Example: --- noop = Noop() print(noop) print(noop()) with noop() as nope: print(nope) with noop as well: print(well) --- Output: --- nope nope nope nope --- IHMO the real question is if we need a Noop.nope() method? Victor 2017-09-09 12:54 GMT-07:00 Victor Stinner <victor.stinner@gmail.com>:

On Sat, Sep 09, 2017 at 02:33:18PM -0700, Victor Stinner <victor.stinner@gmail.com> wrote:
Yep. It must return self so one can chain as many calls as she wants.
Victor
Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

Previous discussion: https://bugs.python.org/issue10049 Issue closed as rejected. Victor 2017-09-09 14:33 GMT-07:00 Victor Stinner <victor.stinner@gmail.com>:

On Sat, Sep 9, 2017 at 10:54 PM, Victor Stinner <victor.stinner@gmail.com> wrote:
This worries me. Clearly, assuming a None-coercing noop, we must have: noop(foo) is None noop[foo] is None noop * foo is None foo * noop is None noop + foo is None foo + noop is None noop - noop is None ... noop / 0 is None ... (noop == None) is None which can all sort of be implicitly extrapolated to be in the PEP. But how are you planning to implement: (noop is None) is None (obj in noop) is None (noop in obj) is None or (None or noop) is None (None and noop) is None and finally: foo(noop) is None ? Sooner or later, someone will need all these features, and the PEP does not seem to address the issue in any way. -- Koos
-- + Koos Zevenhoven + http://twitter.com/k7hoven +

Aha, contextlib.nullcontext() was just added, cool! https://github.com/python/cpython/commit/0784a2e5b174d2dbf7b144d480559e650c5... https://bugs.python.org/issue10049 Victor 2017-09-09 21:54 GMT+02:00 Victor Stinner <victor.stinner@gmail.com>:

On 23 November 2017 at 19:42, Chris Jerdonek <chris.jerdonek@gmail.com> wrote:
There's a lot of runtime complexity hiding behind that "@contextmanager" line, so I'm open to `contextlib` additions that make it possible for sufficiently common patterns to avoid it. (The explicit class based nullcontext() implementation is 7 lines, the same as contextlib.closing()) After 7+ years, I'm happy that this one comes up often enough to be worth a more obvious standard library level answer than we'd previously offered. https://bugs.python.org/issue10049#msg281556 captures the point where I really started changing my mind. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Nov 22, 2017, at 19:32, Victor Stinner <victor.stinner@gmail.com> wrote:
Aha, contextlib.nullcontext() was just added, cool!
So, if I rewrite PEP 559 in terms of decorators it won’t get rejected? from functools import wraps def noop(func): @wraps(func) def wrapper(*args, **kws): return None return wrapper @noop def do_something_important(x, y, z): return blah_blah_blah(x, y, z) print(do_something_important(1, 2, z=3)) Cheers? -Barry

On 24 November 2017 at 01:49, Barry Warsaw <barry@python.org> wrote:
The conceptual delta between knowing how to call "noop()" and how to write "def noop(): pass" is just a *teensy* bit smaller than that between knowing how to use: with nullcontext(value) as var: ... and how to write: @contextlib.contextmanager def nullcontext(enter_result): yield enter_result or: class nullcontext(object): def __init__(self, enter_result): self.enter_result = enter_result def __enter__(self): return self.enter_result def __exit__(self, *args): pass So the deciding line for me was "Should people need to know how to write their own context managers in order to have access to a null context manager?", and I eventually decided the right answer was "No", since the context management protocol is actually reasonably tricky conceptually, and even the simplest version still requires knowing how to use decorators and generators (as well as knowing which specific decorator to use). The conceptual step between calling and writing functions is much smaller, and defining your own re-usable functions is a more fundamental Python skill than defining your own context managers. And while I assume you were mostly joking, the idea of a "@functools.stub_call(result=None)" decorator to temporarily replace an otherwise expensive function call could be a genuinely interesting primitive. `unittest.mock` and other testing libraries already have a bunch of tools along those lines, so the principle at work there would be pattern extraction based on things people already do for themselves. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

09.09.17 21:46, Barry Warsaw пише:
Are there other use cases? PEP 553 still is not approved, and you could use other syntax for disabling breakpoint(), e.g. setting PYTHONBREAKPOINT to an empty value. It looks to me that in all other cases it can be replaced with `lambda *args, **kwds: None` (actually the expression can be even simpler in concrete cases). I can't remember any case when I needed an noop() function (unlike to an identity() function or null context manager).

On Sat, 9 Sep 2017 11:46:30 -0700 Barry Warsaw <barry@python.org> wrote:
You forgot to mention the advantage of battle-testing the noop() function on our buildbot fleet! You don't want to compromise your application code with a weak noop() function.
The C built-in implementation is available as a pull request.
Does it guarantee reentrancy? Regards Antoine.

I can't tell whether this was meant seriously, but I don't think it's worth it. People can easily write their own dummy function and give it any damn semantics they want. Let's reject the PEP. On Sat, Sep 9, 2017 at 11:46 AM, Barry Warsaw <barry@python.org> wrote:
-- --Guido van Rossum (python.org/~guido)

On Sep 9, 2017, at 15:12, Guido van Rossum <guido@python.org> wrote:
I can't tell whether this was meant seriously, but I don't think it's worth it. People can easily write their own dummy function and give it any damn semantics they want. Let's reject the PEP.
Alrighty then! (Yes, it was serious, but I claim post-sprint euphoria/delirium). -Barry

On Sun, Sep 10, 2017 at 8:21 PM, Barry Warsaw <barry@python.org> wrote:
Just for future reference, here's a slightly more serious comment: I think the "pass" statement wasn't mentioned yet, but clearly noop() would be duplication of functionality. So maybe the closest thing without duplication would be to make "pass" an expression which evaluates to a no-op function, but which the compiler could perhaps optimize away if it's a statement by itself, or is a builtin. -- Koos -- + Koos Zevenhoven + http://twitter.com/k7hoven +

On Mon, Sep 11, 2017 at 7:29 AM, Koos Zevenhoven <k7hoven@gmail.com> wrote:
As a language change, definitely not. But I like this idea for PYTHONBREAKPOINT. You set it to the name of a function, or to "pass" if you want nothing to be done. It's a special case that can't possibly conflict with normal usage. ChrisA

On Mon, Sep 11, 2017 at 07:39:07AM +1000, Chris Angelico wrote: [...]
I disagree -- its a confusion of concepts. "pass" is a do-nothing statement, not a value, so you can't set something to pass. Expect a lot of StackOverflow questions asking why this doesn't work: sys.breakpoint = pass In fact, in one sense pass is not even a statement. It has no runtime effect, it isn't compiled into any bytecode. It is a purely syntactic feature to satisfy the parser. Of course env variables are actually strings, so we can choose "pass" to mean "no break point" if we wanted. But I think there are already two perfectly good candidates for that usage which don't mix the concepts of statements and values, the empty string, and None: setenv PYTHONBREAKPOINT="" setenv PYTHONBREAKPOINT=None -- Steve

Don't we already have the mock module for that ? A mowk works as a noop, will be ok with being used as a context manager and allow chaining... Either way, what would a noop function really give you compared to lambda *a, **b: None ? A be bit shorter to write. Maybe faster to run. But do you use it so much that it needs to be included ? It's not rocket science. And as a built-in, noon of the less. If it's coded it in C to gain perfs, then: - let's put it in functools, not in built-in. I often wish for partial to be built-in, but it's not. Honestly the fonctools and itertools module should be autoimported (I always do in my PYTHONSTARTUP). But no pony for us, let's not clutter the global name spaces. - provide it with it's little sister, the identity function. They almost always go hand in hand and doing the whole work of this PEP is a nice opportunity. sorted/max/min/sort and most validation callbacks have an identity function as default parameters. We would have then functools.noop and functools.identity. - provide a pure Python backport. Aternatively, just rewrite part of the mock module in C. You'll get a fast noop, with a lot of features, and as a bonus would speed up a lot of unit tests around here.

I don't think the rationale justifies an entire builtin. You could just use "PYTHONBREAKPOINT=int" to disable, or support "PYTHONBREAKPOINT=0" as I think someone else suggested. I personally can't remember the last time I needed a noop() function. I've more often needed an identity() function, and even that has been proposed several times but shot down, mainly because different people have different needs. (Often "None" provided for function arguments means identity anyway, like for sorted() and filter().) If anything, I think it should be functools.noop() and PYTHONBREAKPOINT=functools.noop I think we need more justification and examples of where this would be helpful to justify a new builtin. -Ben On Sat, Sep 9, 2017 at 2:46 PM, Barry Warsaw <barry@python.org> wrote:

I always wanted this feature (no kidding). Would it be possible to add support for the context manager? with noop(): ... Maybe noop can be an instance of: class Noop: def __enter__(self, *args, **kw): return self def __exit__(self, *args): pass def __call__(self, *args, **kw): return self Victor Le 9 sept. 2017 11:48 AM, "Barry Warsaw" <barry@python.org> a écrit :

I was able to find a real keyboard, so here is a more complete code: --- class Noop: def __call__(self, *args, **kw): return self def __enter__(self, *args, **kw): return self def __exit__(self, *args): return def __repr__(self): return 'nope' --- Example: --- noop = Noop() print(noop) print(noop()) with noop() as nope: print(nope) with noop as well: print(well) --- Output: --- nope nope nope nope --- IHMO the real question is if we need a Noop.nope() method? Victor 2017-09-09 12:54 GMT-07:00 Victor Stinner <victor.stinner@gmail.com>:

On Sat, Sep 09, 2017 at 02:33:18PM -0700, Victor Stinner <victor.stinner@gmail.com> wrote:
Yep. It must return self so one can chain as many calls as she wants.
Victor
Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

Previous discussion: https://bugs.python.org/issue10049 Issue closed as rejected. Victor 2017-09-09 14:33 GMT-07:00 Victor Stinner <victor.stinner@gmail.com>:

On Sat, Sep 9, 2017 at 10:54 PM, Victor Stinner <victor.stinner@gmail.com> wrote:
This worries me. Clearly, assuming a None-coercing noop, we must have: noop(foo) is None noop[foo] is None noop * foo is None foo * noop is None noop + foo is None foo + noop is None noop - noop is None ... noop / 0 is None ... (noop == None) is None which can all sort of be implicitly extrapolated to be in the PEP. But how are you planning to implement: (noop is None) is None (obj in noop) is None (noop in obj) is None or (None or noop) is None (None and noop) is None and finally: foo(noop) is None ? Sooner or later, someone will need all these features, and the PEP does not seem to address the issue in any way. -- Koos
-- + Koos Zevenhoven + http://twitter.com/k7hoven +

Aha, contextlib.nullcontext() was just added, cool! https://github.com/python/cpython/commit/0784a2e5b174d2dbf7b144d480559e650c5... https://bugs.python.org/issue10049 Victor 2017-09-09 21:54 GMT+02:00 Victor Stinner <victor.stinner@gmail.com>:

On 23 November 2017 at 19:42, Chris Jerdonek <chris.jerdonek@gmail.com> wrote:
There's a lot of runtime complexity hiding behind that "@contextmanager" line, so I'm open to `contextlib` additions that make it possible for sufficiently common patterns to avoid it. (The explicit class based nullcontext() implementation is 7 lines, the same as contextlib.closing()) After 7+ years, I'm happy that this one comes up often enough to be worth a more obvious standard library level answer than we'd previously offered. https://bugs.python.org/issue10049#msg281556 captures the point where I really started changing my mind. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Nov 22, 2017, at 19:32, Victor Stinner <victor.stinner@gmail.com> wrote:
Aha, contextlib.nullcontext() was just added, cool!
So, if I rewrite PEP 559 in terms of decorators it won’t get rejected? from functools import wraps def noop(func): @wraps(func) def wrapper(*args, **kws): return None return wrapper @noop def do_something_important(x, y, z): return blah_blah_blah(x, y, z) print(do_something_important(1, 2, z=3)) Cheers? -Barry

On 24 November 2017 at 01:49, Barry Warsaw <barry@python.org> wrote:
The conceptual delta between knowing how to call "noop()" and how to write "def noop(): pass" is just a *teensy* bit smaller than that between knowing how to use: with nullcontext(value) as var: ... and how to write: @contextlib.contextmanager def nullcontext(enter_result): yield enter_result or: class nullcontext(object): def __init__(self, enter_result): self.enter_result = enter_result def __enter__(self): return self.enter_result def __exit__(self, *args): pass So the deciding line for me was "Should people need to know how to write their own context managers in order to have access to a null context manager?", and I eventually decided the right answer was "No", since the context management protocol is actually reasonably tricky conceptually, and even the simplest version still requires knowing how to use decorators and generators (as well as knowing which specific decorator to use). The conceptual step between calling and writing functions is much smaller, and defining your own re-usable functions is a more fundamental Python skill than defining your own context managers. And while I assume you were mostly joking, the idea of a "@functools.stub_call(result=None)" decorator to temporarily replace an otherwise expensive function call could be a genuinely interesting primitive. `unittest.mock` and other testing libraries already have a bunch of tools along those lines, so the principle at work there would be pattern extraction based on things people already do for themselves. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

09.09.17 21:46, Barry Warsaw пише:
Are there other use cases? PEP 553 still is not approved, and you could use other syntax for disabling breakpoint(), e.g. setting PYTHONBREAKPOINT to an empty value. It looks to me that in all other cases it can be replaced with `lambda *args, **kwds: None` (actually the expression can be even simpler in concrete cases). I can't remember any case when I needed an noop() function (unlike to an identity() function or null context manager).

On Sat, 9 Sep 2017 11:46:30 -0700 Barry Warsaw <barry@python.org> wrote:
You forgot to mention the advantage of battle-testing the noop() function on our buildbot fleet! You don't want to compromise your application code with a weak noop() function.
The C built-in implementation is available as a pull request.
Does it guarantee reentrancy? Regards Antoine.

I can't tell whether this was meant seriously, but I don't think it's worth it. People can easily write their own dummy function and give it any damn semantics they want. Let's reject the PEP. On Sat, Sep 9, 2017 at 11:46 AM, Barry Warsaw <barry@python.org> wrote:
-- --Guido van Rossum (python.org/~guido)

On Sep 9, 2017, at 15:12, Guido van Rossum <guido@python.org> wrote:
I can't tell whether this was meant seriously, but I don't think it's worth it. People can easily write their own dummy function and give it any damn semantics they want. Let's reject the PEP.
Alrighty then! (Yes, it was serious, but I claim post-sprint euphoria/delirium). -Barry

On Sun, Sep 10, 2017 at 8:21 PM, Barry Warsaw <barry@python.org> wrote:
Just for future reference, here's a slightly more serious comment: I think the "pass" statement wasn't mentioned yet, but clearly noop() would be duplication of functionality. So maybe the closest thing without duplication would be to make "pass" an expression which evaluates to a no-op function, but which the compiler could perhaps optimize away if it's a statement by itself, or is a builtin. -- Koos -- + Koos Zevenhoven + http://twitter.com/k7hoven +

On Mon, Sep 11, 2017 at 7:29 AM, Koos Zevenhoven <k7hoven@gmail.com> wrote:
As a language change, definitely not. But I like this idea for PYTHONBREAKPOINT. You set it to the name of a function, or to "pass" if you want nothing to be done. It's a special case that can't possibly conflict with normal usage. ChrisA

On Sep 10, 2017, at 14:39, Chris Angelico <rosuav@gmail.com> wrote:
I have working code for `PYTHONBREAKPOINT=0` and `PYTHONBREAKPOINT= ` (i.e. the empty string as given by getenv()) meaning “disable”. I don’t think we also need `PYTHONBREAKPOINT=pass`. Cheers, -Barry

On Mon, Sep 11, 2017 at 07:39:07AM +1000, Chris Angelico wrote: [...]
I disagree -- its a confusion of concepts. "pass" is a do-nothing statement, not a value, so you can't set something to pass. Expect a lot of StackOverflow questions asking why this doesn't work: sys.breakpoint = pass In fact, in one sense pass is not even a statement. It has no runtime effect, it isn't compiled into any bytecode. It is a purely syntactic feature to satisfy the parser. Of course env variables are actually strings, so we can choose "pass" to mean "no break point" if we wanted. But I think there are already two perfectly good candidates for that usage which don't mix the concepts of statements and values, the empty string, and None: setenv PYTHONBREAKPOINT="" setenv PYTHONBREAKPOINT=None -- Steve

Don't we already have the mock module for that ? A mowk works as a noop, will be ok with being used as a context manager and allow chaining... Either way, what would a noop function really give you compared to lambda *a, **b: None ? A be bit shorter to write. Maybe faster to run. But do you use it so much that it needs to be included ? It's not rocket science. And as a built-in, noon of the less. If it's coded it in C to gain perfs, then: - let's put it in functools, not in built-in. I often wish for partial to be built-in, but it's not. Honestly the fonctools and itertools module should be autoimported (I always do in my PYTHONSTARTUP). But no pony for us, let's not clutter the global name spaces. - provide it with it's little sister, the identity function. They almost always go hand in hand and doing the whole work of this PEP is a nice opportunity. sorted/max/min/sort and most validation callbacks have an identity function as default parameters. We would have then functools.noop and functools.identity. - provide a pure Python backport. Aternatively, just rewrite part of the mock module in C. You'll get a fast noop, with a lot of features, and as a bonus would speed up a lot of unit tests around here.
participants (14)
-
Antoine Pitrou
-
Barry Warsaw
-
Ben Hoyt
-
Chris Angelico
-
Chris Jerdonek
-
Guido van Rossum
-
Koos Zevenhoven
-
Michel Desmoulin
-
MRAB
-
Nick Coghlan
-
Oleg Broytman
-
Serhiy Storchaka
-
Steven D'Aprano
-
Victor Stinner