Python's standard library provides the socket and ipaddress modules to inspect network interfaces, resolve hostnames, determine local and public IP addresses, and validate IPv4/IPv6 addresses without installing external dependencies.
Quick Lookup: socket.gethostname() retrieves the machine's local network name, while socket.gethostbyname(hostname) resolves that name to its primary IPv4 address.
The simplest way to inspect the host machine is using socket.gethostname() and socket.gethostbyname():
import socket
try:
hostname = socket.gethostname()
ip_address = socket.gethostbyname(hostname)
print("Host Name: ", hostname)
print("IP Address: ", ip_address)
except socket.gaierror as err:
print(f"Failed to resolve host: {err}")
Host Name: workstation-01
IP Address: 192.168.1.105
Note: Output will vary depending on your operating system, local network router, or virtual machine hosting environment.
Modern production servers often have multiple network interfaces (Ethernet, Wi-Fi, VPN tunnels, Docker bridges) and formal domain names. Python offers two specialized functions to inspect them:
socket.getfqdn() (Fully Qualified Domain Name)Returns the fully qualified domain name for a host, resolving aliases through local DNS records:
import socket
fqdn = socket.getfqdn()
print("FQDN:", fqdn)
socket.gethostbyname_ex() (All Configured IPs)Returns an extended 3-element tuple containing: (hostname, alias_list, ip_address_list):
import socket
host_info = socket.gethostbyname_ex(socket.gethostname())
primary_name, aliases, ip_list = host_info
print("Primary Name:", primary_name)
print("Aliases: ", aliases)
print("All IPs: ", ip_list)
Primary Name: web-server-node
Aliases: ['server.internal.local']
All IPs: ['192.168.1.105', '172.17.0.1', '10.8.0.2']
On Linux, macOS, and container environments (such as Docker or cloud servers), socket.gethostbyname(socket.gethostname()) often returns the loopback address 127.0.0.1 because /etc/hosts maps the local machine name to loopback.
To detect the machine's actual outbound network interface IP on the local network (e.g., 192.168.x.x or 10.x.x.x), use the reliable UDP connection probe technique. This does not actually transmit network packets over the internet; it simply prompts the OS kernel routing table to select the primary network interface:
import socket
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# Connect to a public DNS IP (doesn't send any packets)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
except Exception:
ip = "127.0.0.1"
finally:
s.close()
return ip
print("Actual Outbound LAN IP:", get_local_ip())
Behind a NAT router or cellular connection, your local IP (such as 192.168.1.50) is private. To discover your internet-facing public IP address, query an external echo service using the standard library's urllib.request:
import urllib.request
def get_public_ip():
try:
with urllib.request.urlopen("https://api.ipify.org", timeout=3) as response:
return response.read().decode("utf-8")
except Exception as e:
return f"Unavailable ({e})"
print("Public Internet IP:", get_public_ip())
Python 3 includes the powerful ipaddress module to parse, validate, and inspect IP properties (IPv4 vs. IPv6, private vs. public, loopback, or reserved):
import ipaddress
def inspect_ip(ip_str):
try:
ip = ipaddress.ip_address(ip_str)
return {
"version": f"IPv{ip.version}",
"is_private": ip.is_private,
"is_loopback": ip.is_loopback,
"is_global": ip.is_global,
}
except ValueError:
return "Invalid IP Address format"
print("127.0.0.1: ", inspect_ip("127.0.0.1"))
print("192.168.1.1:", inspect_ip("192.168.1.1"))
print("8.8.8.8: ", inspect_ip("8.8.8.8"))
127.0.0.1: {'version': 'IPv4', 'is_private': True, 'is_loopback': True, 'is_global': False}
192.168.1.1: {'version': 'IPv4', 'is_private': True, 'is_loopback': False, 'is_global': False}
8.8.8.8: {'version': 'IPv4', 'is_private': False, 'is_loopback': False, 'is_global': True}
When resolving domain names or connecting over networks, DNS lookups can fail due to offline interfaces, invalid hostnames, or DNS timeouts. Always catch socket.gaierror (Address-related error) instead of bare except::
import socket
target_hosts = ["google.com", "plus2net.com", "invalid-domain-does-not-exist.xyz"]
for domain in target_hosts:
try:
resolved_ip = socket.gethostbyname(domain)
print(f"✔ {domain} -> {resolved_ip}")
except socket.gaierror as err:
print(f"✘ {domain} resolution failed ({err})")
except socket.timeout:
print(f"⏱ {domain} request timed out")
Let's build a network diagnostic utility that parses and categorizes network addresses, verifying local vs. private vs. public statuses with unit assertions:
import ipaddress
def classify_ip(ip_str):
try:
ip_obj = ipaddress.ip_address(ip_str)
except ValueError:
return "INVALID"
if ip_obj.is_loopback:
return "LOOPBACK"
elif ip_obj.is_private:
return "PRIVATE_LAN"
elif ip_obj.is_global:
return "PUBLIC_WAN"
else:
return "RESERVED"
# Unit Assertions
assert classify_ip("127.0.0.1") == "LOOPBACK"
assert classify_ip("::1") == "LOOPBACK" # IPv6 loopback
assert classify_ip("192.168.0.1") == "PRIVATE_LAN"
assert classify_ip("10.0.0.25") == "PRIVATE_LAN"
assert classify_ip("172.16.5.1") == "PRIVATE_LAN"
assert classify_ip("8.8.8.8") == "PUBLIC_WAN"
assert classify_ip("999.1.2.3") == "INVALID"
print("All IP classification unit assertions passed successfully!")
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.