11[ ![ PyPI version] ( https://badge.fury.io/py/ipdata.svg )] ( https://badge.fury.io/py/ipdata ) ![ GitHub Workflow Status] ( https://img.shields.io/github/actions/workflow/status/ipdata/python/python-publish.yml?branch=master )
22
3+ > ** 🎉 Introducing IPTrie** — A fast, type-safe data structure for IP lookups with longest-prefix matching. Supports both IPv4 and IPv6. [ Learn more →] ( #iptrie )
4+
35# Official Python client library and CLI for the ipdata API
46
57This is a Python client and command line interface (CLI) for the [ ipdata.co] ( https://ipdata.co ) IP Geolocation API. ipdata offers a fast, highly-available API to enrich IP Addresses with Location, Company, Threat Intelligence and numerous other data attributes.
@@ -10,6 +12,43 @@ Visit our [Documentation](https://docs.ipdata.co/) for more examples and tutoria
1012
1113[ ![ asciicast] ( https://asciinema.org/a/371292.svg )] ( https://asciinema.org/a/371292 )
1214
15+ ## Table of Contents
16+
17+ - [ Installation] ( #installation )
18+ - [ Library Usage] ( #library-usage )
19+ - [ Looking up the calling IP Address] ( #looking-up-the-calling-ip-address )
20+ - [ Looking up any IP Address] ( #looking-up-any-ip-address )
21+ - [ Getting only one field] ( #getting-only-one-field )
22+ - [ Getting a number of specific fields] ( #getting-a-number-of-specific-fields )
23+ - [ Bulk Lookups] ( #bulk-lookups )
24+ - [ Using the ipdata CLI] ( #using-the-ipdata-cli )
25+ - [ Windows Installation Notes] ( #windows-installation-notes )
26+ - [ Available commands] ( #available-commands )
27+ - [ Initialize the cli with your API Key] ( #initialize-the-cli-with-your-api-key )
28+ - [ Look up your own IP address] ( #look-up-your-own-ip-address )
29+ - [ Look up any IP address] ( #look-up-any-ip-address-1 )
30+ - [ Copying results to clipboard] ( #copying-results-to-clipboard )
31+ - [ Filtering results by a list of fields] ( #filtering-results-by-a-list-of-fields )
32+ - [ Batch lookup] ( #batch-lookup )
33+ - [ Available Fields] ( #available-fields )
34+ - [ Geofeed tools] ( #geofeed-tools )
35+ - [ IPTrie] ( #iptrie )
36+ - [ Features] ( #features )
37+ - [ Quick Start] ( #quick-start )
38+ - [ IPv6 Support] ( #ipv6-support )
39+ - [ Use Cases] ( #use-cases )
40+ - [ Network Classification] ( #network-classification )
41+ - [ GeoIP Lookup] ( #geoip-lookup )
42+ - [ Access Control Lists] ( #access-control-lists )
43+ - [ API Reference] ( #api-reference )
44+ - [ Constructor] ( #constructor )
45+ - [ Methods] ( #methods )
46+ - [ Exceptions] ( #exceptions )
47+ - [ Input Validation] ( #input-validation )
48+ - [ Performance] ( #performance )
49+ - [ Errors] ( #errors )
50+ - [ Tests] ( #tests )
51+
1352## Installation
1453
1554Install the latest version of the cli with ` pip ` .
525564ipdata validate geofeed.txt
526565` ` `
527566
567+ # # IPTrie
568+
569+ IPTrie is a production-ready, type-safe trie for IP addresses and CIDR prefixes with longest-prefix matching.
570+
571+ # ## Features
572+
573+ - ** Dual-stack support** : Handles both IPv4 and IPv6 addresses seamlessly
574+ - ** Longest-prefix matching** : Automatically finds the most specific matching prefix
575+ - ** Type-safe** : Full generic type support with comprehensive type hints
576+ - ** Pythonic API** : Familiar dictionary-like interface (` []` , ` in` , ` del` , ` len` , iteration)
577+ - ** Input validation** : Robust validation using Python' s `ipaddress` module
578+ - **Custom exceptions**: Clear, specific exceptions for better error handling
579+ - **Well-tested**: Comprehensive test suite with edge cases covered
580+
581+ ### Quick Start
582+
583+ ```python
584+ from ipdata import IPTrie
585+
586+ # Create an IPTrie with string values
587+ ip_trie: IPTrie[str] = IPTrie()
588+
589+ # Add network prefixes
590+ ip_trie["10.0.0.0/8"] = "class-a-private"
591+ ip_trie["10.1.0.0/16"] = "datacenter"
592+ ip_trie["10.1.1.0/24"] = "web-servers"
593+
594+ # Longest-prefix matching
595+ print(ip_trie["10.1.1.100"]) # "web-servers"
596+ print(ip_trie["10.1.2.100"]) # "datacenter"
597+ print(ip_trie["10.2.0.1"]) # "class-a-private"
598+
599+ # Check membership
600+ print("10.1.1.50" in ip_trie) # True
601+ print("192.168.1.1" in ip_trie) # False
602+
603+ # Get the matching prefix
604+ print(ip_trie.parent("10.1.1.100")) # "10.1.1.0/24"
605+
606+ # Safe access with default
607+ print(ip_trie.get("8.8.8.8", "unknown")) # "unknown"
608+ ```
609+
610+ ### IPv6 Support
611+
612+ ```python
613+ ip_trie: IPTrie[str] = IPTrie()
614+
615+ ip_trie["2001:db8::/32"] = "documentation"
616+ ip_trie["2001:db8:1::/48"] = "specific-block"
617+
618+ print(ip_trie["2001:db8:1::1"]) # "specific-block"
619+ print(ip_trie["2001:db8:2::1"]) # "documentation"
620+ ```
621+
622+ ### Use Cases
623+
624+ #### Network Classification
625+
626+ ```python
627+ from ipdata import IPTrie
628+
629+ classifier: IPTrie[dict] = IPTrie()
630+ classifier["10.0.0.0/8"] = {"type": "private", "rfc": "1918"}
631+ classifier["172.16.0.0/12"] = {"type": "private", "rfc": "1918"}
632+ classifier["192.168.0.0/16"] = {"type": "private", "rfc": "1918"}
633+ classifier["0.0.0.0/0"] = {"type": "public", "rfc": None}
634+
635+ def classify_ip(ip: str) -> dict:
636+ return classifier.get(ip, {"type": "unknown"})
637+
638+ print(classify_ip("192.168.1.100")) # {"type": "private", "rfc": "1918"}
639+ print(classify_ip("8.8.8.8")) # {"type": "public", "rfc": None}
640+ ```
641+
642+ #### GeoIP Lookup
643+
644+ ```python
645+ from ipdata import IPTrie
646+
647+ geo_db: IPTrie[str] = IPTrie()
648+ geo_db["8.8.8.0/24"] = "US"
649+ geo_db["1.1.1.0/24"] = "AU"
650+
651+ def get_country(ip: str) -> str:
652+ return geo_db.get(ip, "Unknown")
653+ ```
654+
655+ #### Access Control Lists
656+
657+ ```python
658+ from ipdata import IPTrie
659+
660+ acl: IPTrie[bool] = IPTrie()
661+ acl["192.168.1.0/24"] = True # Allow internal
662+ acl["10.0.0.0/8"] = True # Allow VPN
663+ acl["0.0.0.0/0"] = False # Deny all others
664+
665+ def is_allowed(ip: str) -> bool:
666+ return acl.get(ip, False)
667+ ```
668+
669+ ### API Reference
670+
671+ #### Constructor
672+
673+ ```python
674+ IPTrie[T]() # Create an empty IPTrie with value type T
675+ ```
676+
677+ #### Methods
678+
679+ | Method | Description |
680+ |--------|-------------|
681+ | `__setitem__(key, value)` | Set value for IP/prefix |
682+ | `__getitem__(key)` | Get value using longest-prefix match (raises `KeyNotFoundError`) |
683+ | `get(key, default=None)` | Get value or default if not found |
684+ | `__delitem__(key)` | Delete exact prefix |
685+ | `__contains__(key)` | Check if IP matches any prefix |
686+ | `has_key(key)` | Check if exact prefix exists |
687+ | `parent(key)` | Get the longest matching prefix string |
688+ | `children(key)` | Get all more specific prefixes |
689+ | `__len__()` | Count of all prefixes |
690+ | `__iter__()` | Iterate over all prefixes |
691+ | `keys()` | Iterator over prefixes |
692+ | `values()` | Iterator over values |
693+ | `items()` | Iterator over (prefix, value) tuples |
694+ | `clear()` | Remove all entries |
695+
696+ #### Exceptions
697+
698+ | Exception | Description |
699+ |-----------|-------------|
700+ | `IPTrieError` | Base exception class |
701+ | `InvalidIPError` | Invalid IP address or network format |
702+ | `KeyNotFoundError` | No matching prefix found (also a `KeyError`) |
703+
704+ ### Input Validation
705+
706+ IPTrie validates all inputs using Python' s ` ipaddress` module:
707+
708+ ` ` ` python
709+ from ipdata import IPTrie, InvalidIPError
710+
711+ ip_trie: IPTrie[str] = IPTrie ()
712+
713+ # These work
714+ ip_trie[" 192.168.1.0/24" ] = " valid"
715+ ip_trie[" 2001:db8::1" ] = " valid"
716+
717+ # These raise InvalidIPError
718+ try:
719+ ip_trie[" not-an-ip" ] = " invalid"
720+ except InvalidIPError as e:
721+ print(f" Error: {e}" )
722+
723+ try:
724+ ip_trie[" " ] = " empty"
725+ except InvalidIPError as e:
726+ print(f" Error: {e}" )
727+ ` ` `
728+
729+ # ## Performance
730+
731+ IPTrie uses Patricia tries (via ` pytricia` ) internally, providing:
732+
733+ - ** O(k)** lookup time where k is the prefix length (32 for IPv4, 128 for IPv6)
734+ - ** Memory efficient** storage of overlapping prefixes
735+ - ** Fast iteration** over all prefixes
736+
528737# # Errors
529738
530739A list of possible errors is available at [Status Codes](https://docs.ipdata.co/api-reference/status-codes)
0 commit comments