Skip to content
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Added
- Support [ipapi.co](https://ipapi.co/json/)
### Changed
- `IPv4API.IPAPI` renamed to `IPv4API.IP_API_COM`
- `IPv4API.IPINFO` renamed to `IPv4API.IPINFO_IO`
- `IPv4API.IPSB` renamed to `IPv4API.IP_SB`
- `IPv4API.IDENTME` renamed to `IPv4API.IDENT_ME`
- `IPv4API.TNEDIME` renamed to `IPv4API.TNEDI_ME`
- `README.md` updated
## [0.3] - 2025-05-19
### Added
- `is_ipv4` function
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@

```pycon
>>> from ipspot import get_public_ipv4, IPv4API
>>> get_public_ipv4(api=IPv4API.IPAPI)
>>> get_public_ipv4(api=IPv4API.IP_API_COM)
{'status': True, 'data': {'ip': 'xx.xx.xx.xx', 'api': 'ip-api.com'}}
>>> get_public_ipv4(api=IPv4API.IPAPI, geo=True, timeout=10)
>>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10)
{'data': {'country_code': 'GB', 'latitude': 50.9097, 'longitude': -1.4043, 'api': 'ip-api.com', 'country': 'United Kingdom', 'timezone': 'Europe/London', 'organization': '', 'region': 'England', 'ip': 'xx.xx.xx.xx', 'city': 'Southampton'}, 'status': True}
```

Expand Down Expand Up @@ -147,12 +147,12 @@ Public IP and Location Info:

#### IPv4 API

ℹ️ `ipv4-api` valid choices: [`auto`, `ipapi`, `ipinfo`, `ipsb`, `identme`, `tnedime`, `ipapi_co`]
ℹ️ `ipv4-api` valid choices: [`auto`, `ip-api.com`, `ipinfo.io`, `ip.sb`, `ident.me`, `tnedi.me`, `ipapi.co`]

ℹ️ The default value: `auto`

```console
> ipspot --ipv4-api="ipinfo"
> ipspot --ipv4-api="ipinfo.io"
Private IP:

10.36.18.154
Expand Down
26 changes: 13 additions & 13 deletions ipspot/ipv4.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def get_private_ipv4() -> Dict[str, Union[bool, Dict[str, str], str]]:
return {"status": False, "error": str(e)}


def _ipsb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
def _ip_sb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
"""
Get public IP and geolocation using ip.sb.

Expand Down Expand Up @@ -145,8 +145,8 @@ def _ipapi_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
return {"status": False, "error": str(e)}


def _ipapi_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
def _ip_api_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
"""
Get public IP and geolocation using ip-api.com.

Expand Down Expand Up @@ -180,8 +180,8 @@ def _ipapi_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
return {"status": False, "error": str(e)}


def _ipinfo_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
def _ipinfo_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
"""
Get public IP and geolocation using ipinfo.io.

Expand Down Expand Up @@ -245,8 +245,8 @@ def _ident_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
return {"status": False, "error": str(e)}


def _tnedime_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
def _tnedi_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
"""
Get public IP and geolocation using tnedi.me.

Expand Down Expand Up @@ -286,11 +286,11 @@ def get_public_ipv4(api: IPv4API=IPv4API.AUTO, geo: bool=False,
:param timeout: timeout value for API
"""
api_map = {
IPv4API.IDENTME: _ident_me_ipv4,
IPv4API.TNEDIME: _tnedime_ipv4,
IPv4API.IPSB: _ipsb_ipv4,
IPv4API.IPAPI: _ipapi_ipv4,
IPv4API.IPINFO: _ipinfo_ipv4,
IPv4API.IDENT_ME: _ident_me_ipv4,
IPv4API.TNEDI_ME: _tnedi_me_ipv4,
IPv4API.IP_SB: _ip_sb_ipv4,
IPv4API.IP_API_COM: _ip_api_com_ipv4,
IPv4API.IPINFO_IO: _ipinfo_io_ipv4,
IPv4API.IPAPI_CO: _ipapi_co_ipv4
}

Expand Down
12 changes: 6 additions & 6 deletions ipspot/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ class IPv4API(Enum):
"""Public IPv4 API enum."""

AUTO = "auto"
IPAPI = "ipapi"
IPAPI_CO = "ipapi_co"
IPINFO = "ipinfo"
IPSB = "ipsb"
IDENTME = "identme"
TNEDIME = "tnedime"
IP_API_COM = "ip-api.com"
IPAPI_CO = "ipapi.co"
IPINFO_IO = "ipinfo.io"
IP_SB = "ip.sb"
IDENT_ME = "ident.me"
TNEDI_ME = "tnedi.me"


PARAMETERS_NAME_MAP = {
Expand Down
60 changes: 30 additions & 30 deletions tests/test_ipv4.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,103 +113,103 @@ def test_public_ipv4_ipapi_co_net_error():
assert result["error"] == "No Internet"


def test_public_ipv4_ipapi_success():
result = get_public_ipv4(api=IPv4API.IPAPI, geo=True)
def test_public_ipv4_ip_api_com_success():
result = get_public_ipv4(api=IPv4API.IP_API_COM, geo=True)
assert result["status"]
assert is_ipv4(result["data"]["ip"])
assert set(result["data"].keys()) == DATA_ITEMS
assert result["data"]["api"] == "ip-api.com"


def test_public_ipv4_ipapi_timeout_error():
result = get_public_ipv4(api=IPv4API.IPAPI, geo=True, timeout="5")
def test_public_ipv4_ip_api_com_timeout_error():
result = get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout="5")
assert not result["status"]


def test_public_ipv4_ipapi_net_error():
def test_public_ipv4_ip_api_com_net_error():
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
result = get_public_ipv4(api=IPv4API.IPAPI)
result = get_public_ipv4(api=IPv4API.IP_API_COM)
assert not result["status"]
assert result["error"] == "No Internet"


def test_public_ipv4_ipinfo_success():
result = get_public_ipv4(api=IPv4API.IPINFO, geo=True)
def test_public_ipv4_ipinfo_io_success():
result = get_public_ipv4(api=IPv4API.IPINFO_IO, geo=True)
assert result["status"]
assert is_ipv4(result["data"]["ip"])
assert set(result["data"].keys()) == DATA_ITEMS
assert result["data"]["api"] == "ipinfo.io"


def test_public_ipv4_ipinfo_timeout_error():
result = get_public_ipv4(api=IPv4API.IPINFO, geo=True, timeout="5")
def test_public_ipv4_ipinfo_io_timeout_error():
result = get_public_ipv4(api=IPv4API.IPINFO_IO, geo=True, timeout="5")
assert not result["status"]


def test_public_ipv4_ipinfo_net_error():
def test_public_ipv4_ipinfo_io_net_error():
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
result = get_public_ipv4(api=IPv4API.IPINFO)
result = get_public_ipv4(api=IPv4API.IPINFO_IO)
assert not result["status"]
assert result["error"] == "No Internet"


def test_public_ipv4_ipsb_success():
result = get_public_ipv4(api=IPv4API.IPSB, geo=True, timeout=30)
def test_public_ipv4_ip_sb_success():
result = get_public_ipv4(api=IPv4API.IP_SB, geo=True, timeout=30)
assert result["status"]
assert is_ipv4(result["data"]["ip"])
assert set(result["data"].keys()) == DATA_ITEMS
assert result["data"]["api"] == "ip.sb"


def test_public_ipv4_ipsb_timeout_error():
result = get_public_ipv4(api=IPv4API.IPSB, geo=True, timeout="5")
def test_public_ipv4_ip_sb_timeout_error():
result = get_public_ipv4(api=IPv4API.IP_SB, geo=True, timeout="5")
assert not result["status"]



def test_public_ipv4_ipsb_net_error():
def test_public_ipv4_ip_sb_net_error():
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
result = get_public_ipv4(api=IPv4API.IPSB)
result = get_public_ipv4(api=IPv4API.IP_SB)
assert not result["status"]
assert result["error"] == "No Internet"


def test_public_ipv4_identme_success():
result = get_public_ipv4(api=IPv4API.IDENTME, geo=True)
def test_public_ipv4_ident_me_success():
result = get_public_ipv4(api=IPv4API.IDENT_ME, geo=True)
assert result["status"]
assert is_ipv4(result["data"]["ip"])
assert set(result["data"].keys()) == DATA_ITEMS
assert result["data"]["api"] == "ident.me"


def test_public_ipv4_identme_timeout_error():
result = get_public_ipv4(api=IPv4API.IDENTME, geo=True, timeout="5")
def test_public_ipv4_ident_me_timeout_error():
result = get_public_ipv4(api=IPv4API.IDENT_ME, geo=True, timeout="5")
assert not result["status"]


def test_public_ipv4_identme_net_error():
def test_public_ipv4_ident_me_net_error():
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
result = get_public_ipv4(api=IPv4API.IDENTME)
result = get_public_ipv4(api=IPv4API.IDENT_ME)
assert not result["status"]
assert result["error"] == "No Internet"


def test_public_ipv4_tnedime_success():
result = get_public_ipv4(api=IPv4API.TNEDIME, geo=True)
def test_public_ipv4_tnedi_me_success():
result = get_public_ipv4(api=IPv4API.TNEDI_ME, geo=True)
assert result["status"]
assert is_ipv4(result["data"]["ip"])
assert set(result["data"].keys()) == DATA_ITEMS
assert result["data"]["api"] == "tnedi.me"


def test_public_ipv4_tnedime_timeout_error():
result = get_public_ipv4(api=IPv4API.TNEDIME, geo=True, timeout="5")
def test_public_ipv4_tnedi_me_timeout_error():
result = get_public_ipv4(api=IPv4API.TNEDI_ME, geo=True, timeout="5")
assert not result["status"]


def test_public_ipv4_tnedime_net_error():
def test_public_ipv4_tnedi_me_net_error():
with mock.patch.object(requests.Session, "get", side_effect=Exception("No Internet")):
result = get_public_ipv4(api=IPv4API.TNEDIME)
result = get_public_ipv4(api=IPv4API.TNEDI_ME)
assert not result["status"]
assert result["error"] == "No Internet"

Expand Down