ipdata 4.0.7__tar.gz → 4.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,36 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ipdata
3
- Version: 4.0.7
3
+ Version: 4.0.8
4
4
  Summary: This is the official IPData client library for Python
5
5
  Home-page: https://github.com/ipdata/python
6
6
  Author: Jonathan Kosgei
7
7
  Author-email: jonathan@ipdata.co
8
8
  Project-URL: Bug Tracker, https://github.com/ipdata/python/issues
9
9
  Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
10
14
  Classifier: License :: OSI Approved :: MIT License
11
15
  Classifier: Operating System :: OS Independent
12
16
  Requires-Python: >=3.9
13
17
  Description-Content-Type: text/markdown
14
18
  License-File: LICENSE
19
+ Requires-Dist: requests
20
+ Requires-Dist: rich
21
+ Requires-Dist: click
22
+ Requires-Dist: click_default_group
23
+ Requires-Dist: pyperclip
24
+ Requires-Dist: pytest
25
+ Requires-Dist: hypothesis
26
+ Requires-Dist: python-dotenv
27
+ Requires-Dist: pytricia
28
+ Dynamic: license-file
15
29
 
16
30
  [![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)
17
31
 
32
+ > **🎉 Introducing IPTrie** — A fast, type-safe data structure for IP lookups with longest-prefix matching. Supports both IPv4 and IPv6. [Learn more →](#iptrie)
33
+
18
34
  # Official Python client library and CLI for the ipdata API
19
35
 
20
36
  This 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.
@@ -25,6 +41,43 @@ Visit our [Documentation](https://docs.ipdata.co/) for more examples and tutoria
25
41
 
26
42
  [![asciicast](https://asciinema.org/a/371292.svg)](https://asciinema.org/a/371292)
27
43
 
44
+ ## Table of Contents
45
+
46
+ - [Installation](#installation)
47
+ - [Library Usage](#library-usage)
48
+ - [Looking up the calling IP Address](#looking-up-the-calling-ip-address)
49
+ - [Looking up any IP Address](#looking-up-any-ip-address)
50
+ - [Getting only one field](#getting-only-one-field)
51
+ - [Getting a number of specific fields](#getting-a-number-of-specific-fields)
52
+ - [Bulk Lookups](#bulk-lookups)
53
+ - [Using the ipdata CLI](#using-the-ipdata-cli)
54
+ - [Windows Installation Notes](#windows-installation-notes)
55
+ - [Available commands](#available-commands)
56
+ - [Initialize the cli with your API Key](#initialize-the-cli-with-your-api-key)
57
+ - [Look up your own IP address](#look-up-your-own-ip-address)
58
+ - [Look up any IP address](#look-up-any-ip-address-1)
59
+ - [Copying results to clipboard](#copying-results-to-clipboard)
60
+ - [Filtering results by a list of fields](#filtering-results-by-a-list-of-fields)
61
+ - [Batch lookup](#batch-lookup)
62
+ - [Available Fields](#available-fields)
63
+ - [Geofeed tools](#geofeed-tools)
64
+ - [IPTrie](#iptrie)
65
+ - [Features](#features)
66
+ - [Quick Start](#quick-start)
67
+ - [IPv6 Support](#ipv6-support)
68
+ - [Use Cases](#use-cases)
69
+ - [Network Classification](#network-classification)
70
+ - [GeoIP Lookup](#geoip-lookup)
71
+ - [Access Control Lists](#access-control-lists)
72
+ - [API Reference](#api-reference)
73
+ - [Constructor](#constructor)
74
+ - [Methods](#methods)
75
+ - [Exceptions](#exceptions)
76
+ - [Input Validation](#input-validation)
77
+ - [Performance](#performance)
78
+ - [Errors](#errors)
79
+ - [Tests](#tests)
80
+
28
81
  ## Installation
29
82
 
30
83
  Install the latest version of the cli with `pip`.
@@ -540,6 +593,176 @@ or
540
593
  ipdata validate geofeed.txt
541
594
  ```
542
595
 
596
+ ## IPTrie
597
+
598
+ IPTrie is a production-ready, type-safe trie for IP addresses and CIDR prefixes with longest-prefix matching.
599
+
600
+ ### Features
601
+
602
+ - **Dual-stack support**: Handles both IPv4 and IPv6 addresses seamlessly
603
+ - **Longest-prefix matching**: Automatically finds the most specific matching prefix
604
+ - **Type-safe**: Full generic type support with comprehensive type hints
605
+ - **Pythonic API**: Familiar dictionary-like interface (`[]`, `in`, `del`, `len`, iteration)
606
+ - **Input validation**: Robust validation using Python's `ipaddress` module
607
+ - **Custom exceptions**: Clear, specific exceptions for better error handling
608
+ - **Well-tested**: Comprehensive test suite with edge cases covered
609
+
610
+ ### Quick Start
611
+
612
+ ```python
613
+ from ipdata import IPTrie
614
+
615
+ # Create an IPTrie with string values
616
+ ip_trie: IPTrie[str] = IPTrie()
617
+
618
+ # Add network prefixes
619
+ ip_trie["10.0.0.0/8"] = "class-a-private"
620
+ ip_trie["10.1.0.0/16"] = "datacenter"
621
+ ip_trie["10.1.1.0/24"] = "web-servers"
622
+
623
+ # Longest-prefix matching
624
+ print(ip_trie["10.1.1.100"]) # "web-servers"
625
+ print(ip_trie["10.1.2.100"]) # "datacenter"
626
+ print(ip_trie["10.2.0.1"]) # "class-a-private"
627
+
628
+ # Check membership
629
+ print("10.1.1.50" in ip_trie) # True
630
+ print("192.168.1.1" in ip_trie) # False
631
+
632
+ # Get the matching prefix
633
+ print(ip_trie.parent("10.1.1.100")) # "10.1.1.0/24"
634
+
635
+ # Safe access with default
636
+ print(ip_trie.get("8.8.8.8", "unknown")) # "unknown"
637
+ ```
638
+
639
+ ### IPv6 Support
640
+
641
+ ```python
642
+ ip_trie: IPTrie[str] = IPTrie()
643
+
644
+ ip_trie["2001:db8::/32"] = "documentation"
645
+ ip_trie["2001:db8:1::/48"] = "specific-block"
646
+
647
+ print(ip_trie["2001:db8:1::1"]) # "specific-block"
648
+ print(ip_trie["2001:db8:2::1"]) # "documentation"
649
+ ```
650
+
651
+ ### Use Cases
652
+
653
+ #### Network Classification
654
+
655
+ ```python
656
+ from ipdata import IPTrie
657
+
658
+ classifier: IPTrie[dict] = IPTrie()
659
+ classifier["10.0.0.0/8"] = {"type": "private", "rfc": "1918"}
660
+ classifier["172.16.0.0/12"] = {"type": "private", "rfc": "1918"}
661
+ classifier["192.168.0.0/16"] = {"type": "private", "rfc": "1918"}
662
+ classifier["0.0.0.0/0"] = {"type": "public", "rfc": None}
663
+
664
+ def classify_ip(ip: str) -> dict:
665
+ return classifier.get(ip, {"type": "unknown"})
666
+
667
+ print(classify_ip("192.168.1.100")) # {"type": "private", "rfc": "1918"}
668
+ print(classify_ip("8.8.8.8")) # {"type": "public", "rfc": None}
669
+ ```
670
+
671
+ #### GeoIP Lookup
672
+
673
+ ```python
674
+ from ipdata import IPTrie
675
+
676
+ geo_db: IPTrie[str] = IPTrie()
677
+ geo_db["8.8.8.0/24"] = "US"
678
+ geo_db["1.1.1.0/24"] = "AU"
679
+
680
+ def get_country(ip: str) -> str:
681
+ return geo_db.get(ip, "Unknown")
682
+ ```
683
+
684
+ #### Access Control Lists
685
+
686
+ ```python
687
+ from ipdata import IPTrie
688
+
689
+ acl: IPTrie[bool] = IPTrie()
690
+ acl["192.168.1.0/24"] = True # Allow internal
691
+ acl["10.0.0.0/8"] = True # Allow VPN
692
+ acl["0.0.0.0/0"] = False # Deny all others
693
+
694
+ def is_allowed(ip: str) -> bool:
695
+ return acl.get(ip, False)
696
+ ```
697
+
698
+ ### API Reference
699
+
700
+ #### Constructor
701
+
702
+ ```python
703
+ IPTrie[T]() # Create an empty IPTrie with value type T
704
+ ```
705
+
706
+ #### Methods
707
+
708
+ | Method | Description |
709
+ |--------|-------------|
710
+ | `__setitem__(key, value)` | Set value for IP/prefix |
711
+ | `__getitem__(key)` | Get value using longest-prefix match (raises `KeyNotFoundError`) |
712
+ | `get(key, default=None)` | Get value or default if not found |
713
+ | `__delitem__(key)` | Delete exact prefix |
714
+ | `__contains__(key)` | Check if IP matches any prefix |
715
+ | `has_key(key)` | Check if exact prefix exists |
716
+ | `parent(key)` | Get the longest matching prefix string |
717
+ | `children(key)` | Get all more specific prefixes |
718
+ | `__len__()` | Count of all prefixes |
719
+ | `__iter__()` | Iterate over all prefixes |
720
+ | `keys()` | Iterator over prefixes |
721
+ | `values()` | Iterator over values |
722
+ | `items()` | Iterator over (prefix, value) tuples |
723
+ | `clear()` | Remove all entries |
724
+
725
+ #### Exceptions
726
+
727
+ | Exception | Description |
728
+ |-----------|-------------|
729
+ | `IPTrieError` | Base exception class |
730
+ | `InvalidIPError` | Invalid IP address or network format |
731
+ | `KeyNotFoundError` | No matching prefix found (also a `KeyError`) |
732
+
733
+ ### Input Validation
734
+
735
+ IPTrie validates all inputs using Python's `ipaddress` module:
736
+
737
+ ```python
738
+ from ipdata import IPTrie, InvalidIPError
739
+
740
+ ip_trie: IPTrie[str] = IPTrie()
741
+
742
+ # These work
743
+ ip_trie["192.168.1.0/24"] = "valid"
744
+ ip_trie["2001:db8::1"] = "valid"
745
+
746
+ # These raise InvalidIPError
747
+ try:
748
+ ip_trie["not-an-ip"] = "invalid"
749
+ except InvalidIPError as e:
750
+ print(f"Error: {e}")
751
+
752
+ try:
753
+ ip_trie[""] = "empty"
754
+ except InvalidIPError as e:
755
+ print(f"Error: {e}")
756
+ ```
757
+
758
+ ### Performance
759
+
760
+ IPTrie uses Patricia tries (via `pytricia`) internally, providing:
761
+
762
+ - **O(k)** lookup time where k is the prefix length (32 for IPv4, 128 for IPv6)
763
+ - **Memory efficient** storage of overlapping prefixes
764
+ - **Fast iteration** over all prefixes
765
+
543
766
  ## Errors
544
767
 
545
768
  A list of possible errors is available at [Status Codes](https://docs.ipdata.co/api-reference/status-codes)
@@ -1,20 +1,7 @@
1
- Metadata-Version: 2.1
2
- Name: ipdata
3
- Version: 4.0.7
4
- Summary: This is the official IPData client library for Python
5
- Home-page: https://github.com/ipdata/python
6
- Author: Jonathan Kosgei
7
- Author-email: jonathan@ipdata.co
8
- Project-URL: Bug Tracker, https://github.com/ipdata/python/issues
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.9
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
-
16
1
  [![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)
17
2
 
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
+
18
5
  # Official Python client library and CLI for the ipdata API
19
6
 
20
7
  This 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.
@@ -25,6 +12,43 @@ Visit our [Documentation](https://docs.ipdata.co/) for more examples and tutoria
25
12
 
26
13
  [![asciicast](https://asciinema.org/a/371292.svg)](https://asciinema.org/a/371292)
27
14
 
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
+
28
52
  ## Installation
29
53
 
30
54
  Install the latest version of the cli with `pip`.
@@ -540,6 +564,176 @@ or
540
564
  ipdata validate geofeed.txt
541
565
  ```
542
566
 
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
+
543
737
  ## Errors
544
738
 
545
739
  A list of possible errors is available at [Status Codes](https://docs.ipdata.co/api-reference/status-codes)
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = ipdata
3
- version = 4.0.7
3
+ version = 4.0.8
4
4
  author = Jonathan Kosgei
5
5
  author_email = jonathan@ipdata.co
6
6
  description = This is the official IPData client library for Python
@@ -11,6 +11,10 @@ project_urls =
11
11
  Bug Tracker = https://github.com/ipdata/python/issues
12
12
  classifiers =
13
13
  Programming Language :: Python :: 3.9
14
+ Programming Language :: Python :: 3.10
15
+ Programming Language :: Python :: 3.11
16
+ Programming Language :: Python :: 3.12
17
+ Programming Language :: Python :: 3.13
14
18
  License :: OSI Approved :: MIT License
15
19
  Operating System :: OS Independent
16
20
 
@@ -28,6 +32,7 @@ install_requires =
28
32
  pytest
29
33
  hypothesis
30
34
  python-dotenv
35
+ pytricia
31
36
 
32
37
  [options.entry_points]
33
38
  console_scripts =
@@ -7,6 +7,7 @@
7
7
  >>> ipdata.lookup() # or ipdata.lookup("8.8.8.8")
8
8
  """
9
9
  from .ipdata import IPData
10
+ from .iptrie import IPTrie
10
11
 
11
12
  # Configuration
12
13
  api_key = None