Small, self-contained network utilities: IP/MAC types, DNS lookup, ping, NIC discovery
Project description
netimps
The network utilities every tool ends up rewriting — interface discovery, "which is my IP", reachability checks, CIDR maths, host:port parsing, DNS, ping, scanning and multicast — as one typed, flat-import library.
Built on the standard library: the only runtime dependency is dnspython, and
only resolve() uses it. Interface enumeration, routing and multicast are
ctypes bindings to the platform's own APIs, so there is nothing to compile
and no wheel to miss for your platform.
Features
- Interface discovery, no dependencies —
get_interfaces()gives adapter names, MACs, MTU and real prefix lengths on Linux, macOS/BSD and Windows, viagetifaddrs(3)/GetAdaptersAddresses. Noifaddrrequired. - One parsing entry point —
parse(value, type)with non-raisingtry_parseand booleanis_validsiblings, all typed so a checker narrows the result. MACAddress— colon/hyphen/dot/bare plusint/bytes, hashable and ordered, with.oui,.is_multicast,.is_localand case-selectable rendering.- The socket helpers everyone rewrites —
get_source_ip,get_free_port,tcp_check,wait_for_port. - Routing and MTU —
get_route(first hop, unprivileged),hop_count(raw sockets or traceroute fallback),discover_mtu/get_pmtu,Interface.mtu. - CIDR set maths —
collapseandsubtract, the latter missing fromipaddressentirely. normalize_host—host:portsplitting that gets IPv6 brackets right.- Scanning — concurrent
scan_ports/scan_hosts. - Socket setup —
bind()with the options named,UdpEndpointfor UDP servers that need to know which interface a datagram arrived on, andretry()for the backoff loop everyone writes without jitter. - Multicast —
multicast_sockethandling the join dance whose failure modes are otherwise silent. - DNS and ping —
resolve()returning native types;ping()returning round-trip time and TTL, not just a boolean.
Installation
pip install netimps # the library
pip install netimps[cli] # plus the `netimps` command
Requires Python 3.9+. The CLI extra adds duho; importing the library never
requires it.
Command line
netimps interfaces # names, MACs, MTU, addresses
netimps ping 8.8.8.8 -m tcp -p 443 # icmp | tcp | udp
netimps resolve example.com aaaa
netimps check example.com https # port number or scheme name
netimps mtu 8.8.8.8 # measured, not guessed
netimps scan 192.0.2.0/29 -p common
netimps addr 00:00:5e:00:53:01 # address, network or MAC
netimps split '[::1]:8080' # -> ::1 8080
Every command takes --json. Exit codes are meaningful: 0 success, 1 "the
answer was no", 2 a caller error.
Quick start
import netimps
from netimps import IPAddress, IPNetwork, MACAddress, parse
# Interfaces: names, MACs, MTU and real prefixes on every OS
for iface in netimps.get_interfaces():
print(iface.name, iface.mac, iface.mtu, [str(ip) for ip in iface.ips])
# 'Wi-Fi' 00:00:5e:00:53:01 1500 ['192.0.2.10/24', 'fe80::.../64']
# Types annotate; parse builds
def route(dst: IPAddress, via: IPNetwork) -> None: ...
parse("10.0.0.5") # IPv4Address('10.0.0.5')
parse("10.0.0.5/24", IPNetwork) # IPv4Network('10.0.0.0/24')
netimps.try_parse("nope", IPAddress) # None
netimps.is_valid("::1", IPAddress) # True
# Which of my addresses actually reaches that host?
netimps.get_source_ip("8.8.8.8") # IPv4Address('192.0.2.10')
netimps.get_route("8.8.8.8").gateway # IPv4Address('192.0.2.1')
# Honest reachability, and waiting for a service
netimps.tcp_check("example.com", 443) # True
netimps.wait_for_port("localhost", 5432, timeout=60)
# CIDR set maths
netimps.subtract(["10.0.0.0/24"], ["10.0.0.64/26"])
# [IPv4Network('10.0.0.0/26'), IPv4Network('10.0.0.128/25')]
# host:port, including the IPv6 case people get wrong
netimps.normalize_host("[::1]:8080") # ('::1', 8080)
netimps.normalize_host("::1") # ('::1', None) -- not port 1
# MAC addresses
mac = MACAddress("AA-BB-CC-DD-EE-FF")
mac.as_str("-", upper=True) # 'AA-BB-CC-DD-EE-FF'
mac.is_local, mac.oui.hex() # (True, 'aabbcc') -- AA has the U/L bit
# DNS returns native types
netimps.resolve("example.com")[0].is_global # an IPv4Address, not a str
netimps.resolve("example.com", "txt") # ['v=spf1 -all'] -- unquoted
# ping carries the details, and can use TCP or UDP where ICMP is blocked
result = netimps.ping("8.8.8.8")
result.ok, result.rtt_ms, result.ttl # (True, 9.0, 119)
netimps.ping("8.8.8.8", method="tcp", port=53) # times the handshake
# Path MTU, measured rather than guessed
netimps.discover_mtu("8.8.8.8") # 1500
netimps.discover_mtu("10.0.0.5", method="udp", port=9999)
netimps.get_tcp_mss("example.com", 443) # 1460
# Scanning and multicast
netimps.scan_ports("192.168.1.1", ["ssh", "https"]) # [22, 443]
sock = netimps.multicast_socket("224.0.0.251", 5353) # mDNS listener
# Server-side: bind with the options named, and know where packets came from
server = netimps.bind("", 6767, broadcast=True)
endpoint = netimps.UdpEndpoint(server)
# Retry with backoff and jitter
netimps.retry(lambda: netimps.tcp_check("example.com", 443), attempts=3)
API overview
| Name | Purpose |
|---|---|
IPAddress, IPInterface, IPNetwork |
v4/v6 union aliases for annotations |
IPAddressLike, IPNetworkLike, MACLike |
accepted-input unions |
IPv4Address, IPv4Interface, ... |
stdlib concrete-type re-exports |
parse, try_parse, is_valid |
build a type from a value (raising / None / bool) |
MACAddress |
parse / classify / render MAC addresses |
get_interfaces, Interface, iter_addresses |
native cross-platform NIC discovery |
get_ip, is_link_scoped |
address resolution and scope classification |
collapse, subtract |
CIDR set maths |
normalize_host |
host:port splitting, IPv6-aware |
get_default_port, get_default_scheme, register_port |
scheme ↔ port registry |
resolve |
DNS lookup → native records ([] on failure) |
ping, PingResult |
reachability with RTT and TTL |
bind, bind_error_hint, interface_for |
socket creation and diagnosis |
get_source_ip, get_free_port, tcp_check, wait_for_port |
socket helpers |
UdpEndpoint, Datagram |
UDP receive with arrival interface (IP_PKTINFO) |
Host |
hostname-or-address value type |
retry, backoff_delays |
bounded retry with exponential backoff |
APIPA, LOOPBACK_V4, LOOPBACK_V6, LINK_LOCAL_V6 |
named networks |
get_route, Route, hop_count |
routing and distance |
discover_mtu, get_pmtu, get_tcp_mss |
path MTU by ICMP/UDP/TCP, the kernel's cached guess, or the negotiated MSS |
scan_ports, scan_hosts, PORT_RANGES |
concurrent scanning |
multicast_socket, join_group, leave_group, is_multicast |
multicast |
HOST_DN |
platform.node(), captured at import time |
Full per-export reference, with contracts and gotchas, lives in
src/netimps/AGENTS.md.
Design notes
A few behaviours are deliberate and worth knowing:
Interface.is_loopbackis computed from addresses, not names —lo,lo0andLoopback Pseudo-Interface 1share no spelling.- Concrete types are strict about family.
parse("::1", IPAddress)works;parse("::1", IPv4Address)raises rather than quietly returning v6. - Networks parse non-strict by default, so
10.0.0.5/24normalises instead of raising. Passstrict=Truefor stdlib behaviour. resolveraises on a malformed query rather than returning[]— a typo'd record type should not look like "no such record".ping(ttl=...)behaves the same on every OS. Windowspingexits0for "TTL expired in transit", so the reply address is verified instead of trusting the exit code.hop_countworks unprivileged, falling back to the system traceroute when a raw socket is unavailable.discover_mtumeasures;get_pmtuonly reports what the kernel cached. The latter is usuallyNone, and alwaysNoneon Windows. On one real host the local link was 9000 and the true path MTU 1500 — only probing found it.
Development
python -m venv .venv/dev
.venv/dev/Scripts/pip install -e ".[dev]" # POSIX: .venv/dev/bin/pip
.venv/dev/Scripts/pytest -q
.venv/dev/Scripts/black src/ tests/
Tested on Python 3.9 (the floor) and 3.14.
Releasing
This project follows Semantic Versioning and keeps a
CHANGELOG.md. Pushing a tag matching v* triggers the release
workflow: test gate → build → publish → docs deploy.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netimps-0.0.1.tar.gz.
File metadata
- Download URL: netimps-0.0.1.tar.gz
- Upload date:
- Size: 99.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42912331606f9ef9c97f7dfd8b5caad60a9c2ca71dbbcd565e45758367e3d6b8
|
|
| MD5 |
901727e235b1a2331cc9b960941923c1
|
|
| BLAKE2b-256 |
31d6a91d59b90e4200cbea4243059c5309f5a6ea9627ef035f26db86363641bf
|
Provenance
The following attestation bundles were made for netimps-0.0.1.tar.gz:
Publisher:
release.yml on jose-pr/netimps
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netimps-0.0.1.tar.gz -
Subject digest:
42912331606f9ef9c97f7dfd8b5caad60a9c2ca71dbbcd565e45758367e3d6b8 - Sigstore transparency entry: 2216823446
- Sigstore integration time:
-
Permalink:
jose-pr/netimps@207ae9db5fd847fcb624d2151f563932c23794ed -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/jose-pr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@207ae9db5fd847fcb624d2151f563932c23794ed -
Trigger Event:
push
-
Statement type:
File details
Details for the file netimps-0.0.1-py3-none-any.whl.
File metadata
- Download URL: netimps-0.0.1-py3-none-any.whl
- Upload date:
- Size: 85.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00fb563fb10097244fa259f1a3b8ee4bb9ca78275744525d5cff2d5f55f2b069
|
|
| MD5 |
a1e115c2f658055278f4ebd7c88cfcd3
|
|
| BLAKE2b-256 |
7a8f756197beda268e68850d0fc47a534ef7d5655729c47df330e54d77198ddf
|
Provenance
The following attestation bundles were made for netimps-0.0.1-py3-none-any.whl:
Publisher:
release.yml on jose-pr/netimps
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netimps-0.0.1-py3-none-any.whl -
Subject digest:
00fb563fb10097244fa259f1a3b8ee4bb9ca78275744525d5cff2d5f55f2b069 - Sigstore transparency entry: 2216823493
- Sigstore integration time:
-
Permalink:
jose-pr/netimps@207ae9db5fd847fcb624d2151f563932c23794ed -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/jose-pr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@207ae9db5fd847fcb624d2151f563932c23794ed -
Trigger Event:
push
-
Statement type: