One of the advantages of interpreted languages, like Python, is that when your vendor ships a tool that uses Python… you can see how the Python bits actually work. Or maybe seeing how the sausage is made is a disadvantage?
Delana's vendor tool needs to determine the IP address of the computer in a platform independent way. The "standard" way of doing this in Python is to check the computer's hostname then feed that into a function like one of these which turns it into IP addresses.
Those methods should behave more-or-less the same on both Windows and Linux, so checking the IP address should be a simple and short function. Let's see how Delana's vendor did it:
def get_ip_address(ifname):
os_type = 'windows'
if sys.platform == 'linux' or sys.platform == 'linux2':
os_type = 'linux'
else:
if sys.platform == 'win32':
os_type = 'windows'
if os_type == 'linux':
import fcntl
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
f = fcntl.ioctl(s.fileno(), 35093, struct.pack('256s', ifname[:15]))[20:24]
ip = socket.inet_ntoa(f)
return ip
if os_type == 'windows':
ip = [(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
return ip
Let's start with the OS check. We set os_type = 'windows'
, then check the platform. If it's 'linux'
or 'linux2'
, our os_type
is 'linux'
. Otherwise, if the platform is 'win32'
, we set os_type to 'windows'
, which is what it already was. The entire 'windows'
branch of the if
isn't needed. Also, the "proper" way of writing it would be elif
.
Okay, that's silly, but fine. What if we want to check the IP on Linux? Well, this solution pulls in the fcntl
library, which is a Python wrapper around the fnctl.h
collection of syscalls. They open a socket, get the filehandle, and then do a cryptic ioctl
operation which I'm sure I'd understand if I pored over the manpages for a bit. A quick call to inet_ntoa
turns the integer representing the IP back into a string representing the IP.
From this, I get the sense that the original developer is a C programmer who's doing Python against their will. And more to the point, a Linux C programmer that has an understanding of low-level Linux syscalls. Which is also supported by their approach to this question on Windows.
Before we talk about the Python code, let's just talk about their process. They open a socket to one of Google's DNS servers, then calls getsockname()
to get the IP address. Now, they're not the only ones to use this basic approach. Since this is a UDP socket that they're opening, they're not actually starting a connection, nothing actually gets sent to Google's DNS server (and they could use any IP, since we don't need a valid route). It's weird, but not wrong.
But then there's the actual line, which is such an utter mess of a way of writing this I need to break it into bits to explain the abuse of Python syntax constructs.
Let's start with this bit, as this is easy:
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
That just creates the socket. But we wrap it up in a generator expression:
for s in [
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]
We turn that socket into a list of one item, and then say that we're going to iterate across that, to generate a list of tuples:
[(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
And this is where we're really going into a special place with this code. We have three operations we want to perform. We want to open the socket, check the IP address, and then close the socket. This code does that inside of a tuple: the first item in the tuple is the connect
, then we getsockname
, then we close
.
Finally, we grab the 0th element of the list (the tuple itself), and then the 1st element in the tuple- the result of s.getsockname()[0]
.
The "turn the socket into a list and iterate across that list to generate a new list of one tuple" seems like an attempt to code-golf this down into one line. Maybe they were so angry about having to do a Windows version that they didn't want to spend any more lines of code on it than they had to?
I don't know, but I have a bad feeling this isn't the last time I'm going to encounter someone performing a sequence of operations in a tuple like this.