94

Sometimes I need a dummy context manager that does nothing. It can then be used as a stand-in for a more useful, but optional, context manager. For example:

ctx_mgr = <meaningfulContextManager> if <condition> else <nullContextManager>
with ctx_mgr:
    ...

How do I define such a trivial, empty context manager? Does the Python library offer one off the shelf?

How about cases where we want the context to be used with an as clause?

with ctx_mgr as resource:
    <operations on resource>
Julian
  • 4,170
  • 4
  • 20
  • 27

6 Answers6

124

Python 3.7 and above: use contextlib.nullcontext, specifically designed for this reason.

Before Python 3.7, the standard library does not offer a context manager specifically designed for these use cases, but there are some workarounds.

Since Python 3.4, contextlib.suppress can be used for that purpose in the first case, i.e. when there is no as clause:

ctx_mgr = <meaningfulContextManager> if <condition> else contextlib.suppress()

with ctx_mgr:
    ...

Since Python 3.3, a similar work-around is also available, contextlib.ExitStack, albeit slower than suppress (it takes twice as long in my tests).

Before Python 3.3, or in case you need an as clause before Python 3.7, developers need to roll their own. Here is one possible implementation (see note at the bottom, but all errors are mine):

class NullContextManager(object):
    def __init__(self, dummy_resource=None):
        self.dummy_resource = dummy_resource
    def __enter__(self):
        return self.dummy_resource
    def __exit__(self, *args):
        pass

One can then write:

ctx_mgr = <meaningfulContextManager> if <condition> else NullContextManager(dummy_resource)

with ctx_mgr as resource:
    <operations on resource>

Of course, dummy_resource will need to support all operations required of the "meaningful" resource. So for example, if the meaningful context manager, on __enter__(), returns something that is made to quack() inside the managed block, dummy_resource will also need to support that, albeit possibly without doing anything at all.

class DummyDuck(object):
    def quack()
        # Ssssh...
        pass

ctx_mgr = <meaningfulContextManager> if <condition> else NullContextManager(DummyDuck())

with ctx_mgr as someDuck:
    someDuck.quack()

Source: A Python feature request. Many thanks to all those who contributed to that discussion. This is my attempt at summarising its outcome in a self-answered question, to save people time reading that long thread. Also see Python documentation's mention of this use of ExitStack.

Julian
  • 4,170
  • 4
  • 20
  • 27
  • 2
    The documentation mentions the ExitStack as a do-nothing context manager here https://docs.python.org/3/library/contextlib.html#simplifying-support-for-single-optional-context-managers – Gribouillis Jul 19 '17 at 22:00
  • 4
    **UPD 23 Nov, 2017**, finally, there is a commit with `nullcontext` https://bugs.python.org/issue10049#msg306770 – maxkoryukov Dec 03 '17 at 04:26
  • Many thanks @maxkoryukov, I have updated the answer. – Julian Dec 04 '17 at 07:45
  • 2
    A somewhat faster do-nothing context manager available in 3.4 and up (before `nullcontext` is available) is `contextlib.suppress`. It takes varargs, and you can simply pass no exceptions to it at all: `with suppress():` will suppress nothing, and it's got a lot less overhead than `ExitStack`. – ShadowRanger Mar 01 '18 at 03:08
  • If I want to support before Python 3.3, and if I don't want to pass an argument to `NullContextManager()`, is this solution ok: `class NullContextManager:` `def __enter__(self): pass` `def __exit__(self, *args): pass`? – Basj Nov 23 '20 at 23:10
13

A simple solution for Python 3.6 and below, including 2.7:

from contextlib import contextmanager

@contextmanager
def nullcontext(enter_result=None):
    yield enter_result

Since Python 3.7 you should use the provided contextlib.nullcontext instead.

Martin Valgur
  • 5,793
  • 1
  • 33
  • 45
6

Since Python 3.2, memoryview(b'') can be use as a no-op context manager. See https://docs.python.org/3/library/stdtypes.html#memoryview.release.

Pros

  • No imports required

  • Works on 3.2+

  • Roughly twice as fast as contextlib.nullcontext

Cons

  • You probably want to add a # no-op comment.
Huazuo Gao
  • 1,603
  • 14
  • 20
1

Simple solution for Python 2.7, not yet mentioned in the answers:

from contextlib import nested
with nested():
    ...
Dmitry Shintyakov
  • 1,664
  • 1
  • 11
  • 12
0

For Python 2.7+ , you can use

import contextlib
with (lambda noop_func: contextlib.contextmanager(noop_func))(lambda: (yield))():
    print("hi")
cowlinator
  • 7,195
  • 6
  • 41
  • 61
  • Why the downvote? This works, and answers the question accurately. It's an in-line no-op context manager – cowlinator Nov 26 '20 at 02:27
  • [I didn't downvote] hey found your answer, thought you might like to see mine from before I found this stackoverflow post: `namedtuple("FreeScope",("scope",))(lambda:type("FreeContext",tuple(),{"__enter__":lambda_,__=None,___=None,____=None:None,"__exit__":lambda_,__=None,___=None,____=None:None,},))` – Samuel Marks Dec 19 '20 at 11:51
-5

I just used threading.Lock() as a dummy context manager. Temporary lock, only used by the context manager.