getservbyport also exists.
If we do it for /etc/services, then why not /etc/protocols?
It doesn’t make sense to make a multiplication table: getservbyport, getservbyname, getprotobyname, but not getprotobynumber. It creates the implicit expectation by users something exists only for them to find out it doesn’t.
Warning: I am aware this is a duplicate or reopening on here. Maybe it was rejected last time but it has been half a decade since.
I’ll be honest, I have never used getservbyport that I can ever recall. How useful is getprotobynumber? Do you have a situation where this will be of value?
There is an underlying C function though so this shouldn’tbe too hard, if someone wanted to do it.
I don’t strictly need it, but if it existed I would use it. For example, when analyzing network traffic metadata, I’d prefer to translate protocol numbers to names using the standard library rather than maintaining my own mapping or parsing /etc/protocols in Python. That makes reports more readable and relies on the system’s existing protocol database.
My main point is API consistency. We already have getservbyname/getservbyport and getprotobyname; getprotobynumber seems like the natural missing counterpart. It’s not an essential feature, but it would complete the interface and match user expectations.
Here is the code I want to eliminate:
_PROTO_NAMES_CACHE = None
def _load_proto_names():
"""Parse /etc/protocols into a {number: NAME} mapping.
socket.getprotobynumber() does not exist in Python's standard
library (only the reverse, getprotobyname(), was ever added), so
we build the reverse lookup ourselves. Returns an empty dict if
/etc/protocols isn't available (e.g. on Windows).
"""
mapping = {}
try:
with open("/etc/protocols", encoding="utf-8") as f:
for line in f:
line = line.split("#", 1)[0].strip()
if not line:
continue
parts = line.split()
if len(parts) < 2:
continue
try:
mapping[int(parts[1])] = parts[0].upper()
except ValueError:
continue
except OSError:
pass
return mapping
def proto_name_for(number):
"""Look up the well-known protocol name for a number from /etc/protocols.
Falls back to the bare assigned number (as a string) if there is no
registered name for that protocol.
"""
global _PROTO_NAMES_CACHE
if _PROTO_NAMES_CACHE is None:
_PROTO_NAMES_CACHE = _load_proto_names()
return _PROTO_NAMES_CACHE.get(number, str(number))
While here is the output I want to be trivial for even beginners:
Num packets: 999914, Num bytes: 366325065
IP Protocols:
1 ICMP : 6794
6 TCP : 950654
17 UDP : 38332
47 GRE : 2626
50 IPV6-CRYPT: 1484
89 OSPF : 24
Rare use-cases are a fact. But please also consider that the socket module is different. Actually, it is full of objects that only few people need, because it makes low-level OS functions accesible to Python.
The manual page of getprotobynumber() is scary: the API is unsafe when used in more than one thread. We try to avoid such function in Python since it can cause too many issues (crashes) in multi-threaded applications.
ATTRIBUTES
For an explanation of the terms used in this section, see attrib‐
utes(7).
┌────────────────────┬───────────────┬──────────────────────────┐
│ Interface │ Attribute │ Value │
├────────────────────┼───────────────┼──────────────────────────┤
│ getprotoent() │ Thread safety │ MT-Unsafe race:protoent │
│ │ │ race:protoentbuf locale │
├────────────────────┼───────────────┼──────────────────────────┤
│ getprotobyname() │ Thread safety │ MT-Unsafe │
│ │ │ race:protobyname locale │
├────────────────────┼───────────────┼──────────────────────────┤
│ getprotobynumber() │ Thread safety │ MT-Unsafe │
│ │ │ race:protobynumber │
│ │ │ locale │
├────────────────────┼───────────────┼──────────────────────────┤
│ setprotoent(), │ Thread safety │ MT-Unsafe race:protoent │
│ endprotoent() │ │ locale │
└────────────────────┴───────────────┴──────────────────────────┘
In the above table, protoent in race:protoent signifies that if
any of the functions setprotoent(), getprotoent(), or endpro‐
toent() are used in parallel in different threads of a program,
then data races could occur.
IMO your short Python _load_proto_names() function parsing /etc/protocols is safer and better than the C API. If you consider that other people are looking for a similar function, you can publish it as a PyPI package.
My main point is API consistency. We already have getservbyname/getservbyport and getprotobyname; getprotobynumber seems like the natural missing counterpart. It’s not an essential feature, but it would complete the interface and match user expectations.