53

It seems like it would be only natural to do something like:

with socket(socket.AF_INET, socket.SOCK_DGRAM) as s:

but Python doesn't implement a context manager for socket. Can I easily use it as a context manager, and if so, how?

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
ChaimKut
  • 2,759
  • 3
  • 38
  • 64
  • "Why" questions are in general not good questions for SO. Perhaps you can rewrite this into "how"? :-) – Lennart Regebro May 27 '13 at 11:52
  • @msw: No. That's just a why-question in disguise. A good question is "How do I use a socket as a context manager?". The current question I could correctly answer with. "It does" or "No", or "Yes". Not very helpful. – Lennart Regebro May 27 '13 at 11:57
  • @msw: Firstly, this is just more confirmation that "why" questions are bad, since they can trigger an infinite "why not" descent into hell. :-) Secondly, I think you misunderstand what context managers are. The answer "a socket is not like a file" doesn't make any sense as an answer to the "why" question in this case. It's completely irrelevant that it is not like a file. Context managers are not just for files. – Lennart Regebro May 27 '13 at 12:04
  • 3
    @msw: I didn't alter the meaning, I made it a good question. – Lennart Regebro May 27 '13 at 13:35
  • 1
    @msw: Note that closing is really a temporary state; improve the question and it can be reopened again. Improving the question *before* it is closed is a better idea still. – Martijn Pieters May 27 '13 at 13:49

3 Answers3

91

The socket module is fairly low-level, giving you almost direct access to the C library functionality.

You can always use the contextlib.contextmanager decorator to build your own:

import socket
from contextlib import contextmanager

@contextmanager
def socketcontext(*args, **kw):
    s = socket.socket(*args, **kw)
    try:
        yield s
    finally:
        s.close()

with socketcontext(socket.AF_INET, socket.SOCK_DGRAM) as s:

or use contextlib.closing() to achieve the same effect:

from contextlib import closing

with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s:

but the contextmanager() decorator gives you the opportunity to do other things with the socket first.

Python 3.x does make socket() a context manager, but the documentation wasn't updated to reflect this until well into the Python 3.5 cycle, in 2016. See the socket class in the source code, which adds __enter__ and __exit__ methods.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 3
    the Python3 `with`/`as` seems very useful, I can't think of any reason to leave this out of the docs, would you happen to know why it's not in there? – Ryan Haining Apr 01 '14 at 18:24
  • 2
    @RyanHaining: The [patch submitter](http://bugs.python.org/issue9794) merely forgot to document it properly. – Martijn Pieters Apr 01 '14 at 18:26
  • 3
    @RyanHaining: The patch did update [`socket.create_connection()`](https://docs.python.org/3/library/socket.html#socket.create_connection) but it's kinda hidden. – Martijn Pieters Apr 01 '14 at 18:27
  • what is meant by "first" where it says "gives you the opportunity to do other things with the socket first"? Seems to me that the custom context manager (i.e. socketcontext) allows one to do exactly the same things as contextlib.closing. What is the difference?? – allyourcode May 31 '16 at 18:39
  • 2
    @allyourcode: `contextlib.closing()` will only call `close` on the socket. A custom context manager could, say, bind to an open port, set a timeout or other socket options first, or send out a protocol-specific closing packet or some such when exiting the context. – Martijn Pieters May 31 '16 at 19:15
  • This needs to be `with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as s:` – iepathos Nov 08 '17 at 19:55
  • @Lunulata: indeed; I corrected the other reference too. Thanks! – Martijn Pieters Nov 09 '17 at 10:49
  • The [python documentation](https://docs.python.org/3/library/socket.html#socket-objects) actually states explicit support for the context manager protocol. Not sure if it did back in '13, but at least now it does. – MikeVe Oct 07 '20 at 18:02
  • @MikeVe: it didn't in but 2013. Note that in 2014 we discussed this in the comments here, how the patch submitter forgot to update the docs. The docs were eventually fixed [in 2016](https://github.com/python/cpython/commit/e37fc18b3c0d5fe13d75f37d9ae9c4387a46ee3d). – Martijn Pieters Oct 07 '20 at 19:37
35

The socket module is just a wrapper around the BSD socket interface. It's low-level, and does not really attempt to provide you with a handy or easy to use Pythonic API. You may want to use something higher-level.

That said, it does in fact implement a context manager:

>>> with socket.socket() as s:
...   print(s)
... 
<socket.socket object, fd=3, family=2, type=1, proto=0>

But you need to use Python 3.

For Python 2 compatibility you can use contextlib.

from contextlib import closing
import socket

with closing(socket.socket()) as s:
    print s
Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
4

Please have a look on following snippets, for both TCP and UDP sockets

import socket
from contextlib import contextmanager


@contextmanager
def tcp_connection_to(*args, **kwargs):
    s = socket.create_connection(*args, **kwargs)
    yield s
    s.close()


@contextmanager
def udp_connection():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    yield s
    s.close()

So that you can use them in following way:

MY_SERVER = ('localhost', 5000)   # Yes, we need tuple here
some_data = bytes("Hello.")

with tcp_connection_to(MY_SERVER) as conn:
    conn.send(some_data)

with udp_connection() as conn:
    conn.sendto(some_data, MY_SERVER)

I've also tried to emphasise the difference in behaviour and approach to term 'connection' between TCP and UDP in method names.

mieciu
  • 448
  • 3
  • 7