ipspot 0.2__py3-none-any.whl → 0.4__py3-none-any.whl

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.
ipspot/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """ipspot modules."""
3
3
  from .params import IPSPOT_VERSION, IPv4API
4
- from .functions import get_private_ipv4, get_public_ipv4
4
+ from .ipv4 import get_private_ipv4, get_public_ipv4, is_ipv4
5
+ from .utils import is_loopback
5
6
  __version__ = IPSPOT_VERSION
ipspot/__main__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """ipspot main."""
3
- from .functions import main
3
+ from .cli import main
4
4
 
5
5
 
6
6
  if __name__ == "__main__":
ipspot/cli.py ADDED
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+ """ipspot CLI."""
3
+ import argparse
4
+ from typing import Union, Tuple
5
+ from art import tprint
6
+ from .ipv4 import get_public_ipv4, get_private_ipv4
7
+ from .utils import _filter_parameter
8
+ from .params import IPv4API, PARAMETERS_NAME_MAP
9
+ from .params import IPSPOT_OVERVIEW, IPSPOT_REPO, IPSPOT_VERSION
10
+
11
+
12
+ def ipspot_info() -> None: # pragma: no cover
13
+ """Print ipspot details."""
14
+ tprint("IPSpot")
15
+ tprint("V:" + IPSPOT_VERSION)
16
+ print(IPSPOT_OVERVIEW)
17
+ print("Repo : " + IPSPOT_REPO)
18
+
19
+
20
+ def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO_SAFE, geo: bool=False,
21
+ timeout: Union[float, Tuple[float, float]]=5,
22
+ max_retries: int = 0, retry_delay: float = 1.0) -> None: # pragma: no cover
23
+ """
24
+ Print collected IP and location data.
25
+
26
+ :param ipv4_api: public IPv4 API
27
+ :param geo: geolocation flag
28
+ :param timeout: timeout value for API
29
+ :param max_retries: number of retries
30
+ :param retry_delay: delay between retries (in seconds)
31
+ """
32
+ private_result = get_private_ipv4()
33
+ print("Private IP:\n")
34
+ print(" IP: {private_result[data][ip]}".format(private_result=private_result) if private_result["status"]
35
+ else " Error: {private_result[error]}".format(private_result=private_result))
36
+
37
+ public_title = "\nPublic IP"
38
+ if geo:
39
+ public_title += " and Location Info"
40
+ public_title += ":\n"
41
+ print(public_title)
42
+ public_result = get_public_ipv4(
43
+ ipv4_api,
44
+ geo=geo,
45
+ timeout=timeout,
46
+ max_retries=max_retries,
47
+ retry_delay=retry_delay)
48
+ if public_result["status"]:
49
+ for name, parameter in sorted(public_result["data"].items()):
50
+ print(
51
+ " {name}: {parameter}".format(
52
+ name=PARAMETERS_NAME_MAP[name],
53
+ parameter=_filter_parameter(parameter)))
54
+ else:
55
+ print(" Error: {public_result[error]}".format(public_result=public_result))
56
+
57
+
58
+ def main() -> None: # pragma: no cover
59
+ """CLI main function."""
60
+ parser = argparse.ArgumentParser()
61
+ parser.add_argument(
62
+ '--ipv4-api',
63
+ help='public IPv4 API',
64
+ type=str.lower,
65
+ choices=[
66
+ x.value for x in IPv4API],
67
+ default=IPv4API.AUTO_SAFE.value)
68
+ parser.add_argument('--info', help='info', nargs="?", const=1)
69
+ parser.add_argument('--version', help='version', nargs="?", const=1)
70
+ parser.add_argument('--no-geo', help='no geolocation data', nargs="?", const=1, default=False)
71
+ parser.add_argument('--timeout', help='timeout for the API request', type=float, default=5.0)
72
+ parser.add_argument('--max-retries', help='number of retries', type=int, default=0)
73
+ parser.add_argument('--retry-delay', help='delay between retries (in seconds)', type=float, default=1.0)
74
+
75
+ args = parser.parse_args()
76
+ if args.version:
77
+ print(IPSPOT_VERSION)
78
+ elif args.info:
79
+ ipspot_info()
80
+ else:
81
+ ipv4_api = IPv4API(args.ipv4_api)
82
+ geo = not args.no_geo
83
+ display_ip_info(
84
+ ipv4_api=ipv4_api,
85
+ geo=geo,
86
+ timeout=args.timeout,
87
+ max_retries=args.max_retries,
88
+ retry_delay=args.retry_delay)
ipspot/ipv4.py ADDED
@@ -0,0 +1,540 @@
1
+ # -*- coding: utf-8 -*-
2
+ """ipspot ipv4 functions."""
3
+ import ipaddress
4
+ import socket
5
+ from typing import Union, Dict, List, Tuple
6
+ import requests
7
+ from requests.adapters import HTTPAdapter
8
+ from urllib3.poolmanager import PoolManager
9
+ from .utils import is_loopback, _get_json_standard, _attempt_with_retries
10
+ from .params import REQUEST_HEADERS, IPv4API
11
+
12
+
13
+ class IPv4HTTPAdapter(HTTPAdapter):
14
+ """A custom HTTPAdapter that enforces the use of IPv4 for DNS resolution during HTTP(S) requests using the requests library."""
15
+
16
+ def init_poolmanager(self, connections: int, maxsize: int, block: bool = False, **kwargs: dict) -> None:
17
+ """
18
+ Initialize the connection pool manager using a temporary override of socket.getaddrinfo to ensure only IPv4 addresses are used.
19
+
20
+ :param connections: the number of connection pools to cache
21
+ :param maxsize: the maximum number of connections to save in the pool
22
+ :param block: whether the connections should block when reaching the max size
23
+ :param kwargs: additional keyword arguments for the PoolManager
24
+ """
25
+ self.poolmanager = PoolManager(
26
+ num_pools=connections,
27
+ maxsize=maxsize,
28
+ block=block,
29
+ socket_options=self._ipv4_socket_options(),
30
+ **kwargs
31
+ )
32
+
33
+ def _ipv4_socket_options(self) -> list:
34
+ """
35
+ Temporarily patches socket.getaddrinfo to filter only IPv4 addresses (AF_INET).
36
+
37
+ :return: an empty list of socket options; DNS patching occurs here
38
+ """
39
+ original_getaddrinfo = socket.getaddrinfo
40
+
41
+ def ipv4_only_getaddrinfo(*args: list, **kwargs: dict) -> List[Tuple]:
42
+ results = original_getaddrinfo(*args, **kwargs)
43
+ return [res for res in results if res[0] == socket.AF_INET]
44
+
45
+ self._original_getaddrinfo = socket.getaddrinfo
46
+ socket.getaddrinfo = ipv4_only_getaddrinfo
47
+
48
+ return []
49
+
50
+ def __del__(self) -> None:
51
+ """Restores the original socket.getaddrinfo function upon adapter deletion."""
52
+ if hasattr(self, "_original_getaddrinfo"):
53
+ socket.getaddrinfo = self._original_getaddrinfo
54
+
55
+
56
+ def _get_json_ipv4_forced(url: str, timeout: Union[float, Tuple[float, float]]) -> dict:
57
+ """
58
+ Send GET request with forced IPv4 using IPv4HTTPAdapter that returns JSON response.
59
+
60
+ :param url: API url
61
+ :param timeout: timeout value for API
62
+ """
63
+ with requests.Session() as session:
64
+ session.mount("http://", IPv4HTTPAdapter())
65
+ session.mount("https://", IPv4HTTPAdapter())
66
+ response = session.get(url, headers=REQUEST_HEADERS, timeout=timeout)
67
+ response.raise_for_status()
68
+ return response.json()
69
+
70
+
71
+ def is_ipv4(ip: str) -> bool:
72
+ """
73
+ Check if the given input is a valid IPv4 address.
74
+
75
+ :param ip: input IP
76
+ """
77
+ if not isinstance(ip, str):
78
+ return False
79
+ try:
80
+ _ = ipaddress.IPv4Address(ip)
81
+ return True
82
+ except Exception:
83
+ return False
84
+
85
+
86
+ def get_private_ipv4() -> Dict[str, Union[bool, Dict[str, str], str]]:
87
+ """Retrieve the private IPv4 address."""
88
+ try:
89
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
90
+ s.connect(('192.168.1.1', 1))
91
+ private_ip = s.getsockname()[0]
92
+ if is_ipv4(private_ip) and not is_loopback(private_ip):
93
+ return {"status": True, "data": {"ip": private_ip}}
94
+ return {"status": False, "error": "Could not identify a non-loopback IPv4 address for this system."}
95
+ except Exception as e:
96
+ return {"status": False, "error": str(e)}
97
+
98
+
99
+ def _ip_sb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
100
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
101
+ """
102
+ Get public IP and geolocation using ip.sb.
103
+
104
+ :param geo: geolocation flag
105
+ :param timeout: timeout value for API
106
+ """
107
+ try:
108
+ data = _get_json_standard(url="https://api-ipv4.ip.sb/geoip", timeout=timeout)
109
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ip.sb"}}
110
+ if geo:
111
+ geo_data = {
112
+ "city": data.get("city"),
113
+ "region": data.get("region"),
114
+ "country": data.get("country"),
115
+ "country_code": data.get("country_code"),
116
+ "latitude": data.get("latitude"),
117
+ "longitude": data.get("longitude"),
118
+ "organization": data.get("organization"),
119
+ "timezone": data.get("timezone")
120
+ }
121
+ result["data"].update(geo_data)
122
+ return result
123
+ except Exception as e:
124
+ return {"status": False, "error": str(e)}
125
+
126
+
127
+ def _ipleak_net_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
128
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
129
+ """
130
+ Get public IP and geolocation using ipleak.net.
131
+
132
+ :param geo: geolocation flag
133
+ :param timeout: timeout value for API
134
+ """
135
+ try:
136
+ data = _get_json_standard(url="https://ipv4.ipleak.net/json/", timeout=timeout)
137
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipleak.net"}}
138
+ if geo:
139
+ geo_data = {
140
+ "city": data.get("city_name"),
141
+ "region": data.get("region_name"),
142
+ "country": data.get("country_name"),
143
+ "country_code": data.get("country_code"),
144
+ "latitude": data.get("latitude"),
145
+ "longitude": data.get("longitude"),
146
+ "organization": data.get("isp_name"),
147
+ "timezone": data.get("time_zone")
148
+ }
149
+ result["data"].update(geo_data)
150
+ return result
151
+ except Exception as e:
152
+ return {"status": False, "error": str(e)}
153
+
154
+
155
+ def _my_ip_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
156
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
157
+ """
158
+ Get public IP and geolocation using my-ip.io.
159
+
160
+ :param geo: geolocation flag
161
+ :param timeout: timeout value for API
162
+ """
163
+ try:
164
+ data = _get_json_standard(url="https://api4.my-ip.io/v2/ip.json", timeout=timeout)
165
+ result = {"status": True, "data": {"ip": data["ip"], "api": "my-ip.io"}}
166
+ if geo:
167
+ geo_data = {
168
+ "city": data.get("city"),
169
+ "region": data.get("region"),
170
+ "country": data.get("country", {}).get("name"),
171
+ "country_code": data.get("country", {}).get("code"),
172
+ "latitude": data.get("location", {}).get("lat"),
173
+ "longitude": data.get("location", {}).get("lon"),
174
+ "organization": data.get("asn", {}).get("name"),
175
+ "timezone": data.get("timeZone")
176
+ }
177
+ result["data"].update(geo_data)
178
+ return result
179
+ except Exception as e:
180
+ return {"status": False, "error": str(e)}
181
+
182
+
183
+ def _ifconfig_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
184
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
185
+ """
186
+ Get public IP and geolocation using ifconfig.co.
187
+
188
+ :param geo: geolocation flag
189
+ :param timeout: timeout value for API
190
+ """
191
+ try:
192
+ data = _get_json_ipv4_forced(url="https://ifconfig.co/json", timeout=timeout)
193
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ifconfig.co"}}
194
+ if geo:
195
+ geo_data = {
196
+ "city": data.get("city"),
197
+ "region": data.get("region_name"),
198
+ "country": data.get("country"),
199
+ "country_code": data.get("country_iso"),
200
+ "latitude": data.get("latitude"),
201
+ "longitude": data.get("longitude"),
202
+ "organization": data.get("asn_org"),
203
+ "timezone": data.get("time_zone")
204
+ }
205
+ result["data"].update(geo_data)
206
+ return result
207
+ except Exception as e:
208
+ return {"status": False, "error": str(e)}
209
+
210
+
211
+ def _ipapi_co_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
212
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
213
+ """
214
+ Get public IP and geolocation using ipapi.co.
215
+
216
+ :param geo: geolocation flag
217
+ :param timeout: timeout value for API
218
+ """
219
+ try:
220
+ data = _get_json_ipv4_forced(url="https://ipapi.co/json/", timeout=timeout)
221
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipapi.co"}}
222
+ if geo:
223
+ geo_data = {
224
+ "city": data.get("city"),
225
+ "region": data.get("region"),
226
+ "country": data.get("country_name"),
227
+ "country_code": data.get("country_code"),
228
+ "latitude": data.get("latitude"),
229
+ "longitude": data.get("longitude"),
230
+ "organization": data.get("org"),
231
+ "timezone": data.get("timezone")
232
+ }
233
+ result["data"].update(geo_data)
234
+ return result
235
+ except Exception as e:
236
+ return {"status": False, "error": str(e)}
237
+
238
+
239
+ def _ip_api_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
240
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
241
+ """
242
+ Get public IP and geolocation using ip-api.com.
243
+
244
+ :param geo: geolocation flag
245
+ :param timeout: timeout value for API
246
+ """
247
+ try:
248
+ data = _get_json_ipv4_forced(url="http://ip-api.com/json/", timeout=timeout)
249
+ if data.get("status") != "success":
250
+ return {"status": False, "error": "ip-api lookup failed"}
251
+ result = {"status": True, "data": {"ip": data["query"], "api": "ip-api.com"}}
252
+ if geo:
253
+ geo_data = {
254
+ "city": data.get("city"),
255
+ "region": data.get("regionName"),
256
+ "country": data.get("country"),
257
+ "country_code": data.get("countryCode"),
258
+ "latitude": data.get("lat"),
259
+ "longitude": data.get("lon"),
260
+ "organization": data.get("org"),
261
+ "timezone": data.get("timezone")
262
+ }
263
+ result["data"].update(geo_data)
264
+ return result
265
+ except Exception as e:
266
+ return {"status": False, "error": str(e)}
267
+
268
+
269
+ def _ipinfo_io_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
270
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
271
+ """
272
+ Get public IP and geolocation using ipinfo.io.
273
+
274
+ :param geo: geolocation flag
275
+ :param timeout: timeout value for API
276
+ """
277
+ try:
278
+ data = _get_json_ipv4_forced(url="https://ipinfo.io/json", timeout=timeout)
279
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ipinfo.io"}}
280
+ if geo:
281
+ loc = data.get("loc", "").split(",")
282
+ geo_data = {
283
+ "city": data.get("city"),
284
+ "region": data.get("region"),
285
+ "country": None,
286
+ "country_code": data.get("country"),
287
+ "latitude": float(loc[0]) if len(loc) == 2 else None,
288
+ "longitude": float(loc[1]) if len(loc) == 2 else None,
289
+ "organization": data.get("org"),
290
+ "timezone": data.get("timezone")
291
+ }
292
+ result["data"].update(geo_data)
293
+ return result
294
+ except Exception as e:
295
+ return {"status": False, "error": str(e)}
296
+
297
+
298
+ def _reallyfreegeoip_org_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
299
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
300
+ """
301
+ Get public IP and geolocation using reallyfreegeoip.org.
302
+
303
+ :param geo: geolocation flag
304
+ :param timeout: timeout value for API
305
+ """
306
+ try:
307
+ data = _get_json_ipv4_forced(url="https://reallyfreegeoip.org/json/", timeout=timeout)
308
+ result = {"status": True, "data": {"ip": data["ip"], "api": "reallyfreegeoip.org"}}
309
+ if geo:
310
+ geo_data = {
311
+ "city": data.get("city"),
312
+ "region": data.get("region_name"),
313
+ "country": data.get("country_name"),
314
+ "country_code": data.get("country_code"),
315
+ "latitude": data.get("latitude"),
316
+ "longitude": data.get("longitude"),
317
+ "organization": None, # does not provide organization
318
+ "timezone": data.get("time_zone")
319
+ }
320
+ result["data"].update(geo_data)
321
+ return result
322
+ except Exception as e:
323
+ return {"status": False, "error": str(e)}
324
+
325
+
326
+ def _ident_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
327
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
328
+ """
329
+ Get public IP and geolocation using ident.me.
330
+
331
+ :param geo: geolocation flag
332
+ :param timeout: timeout value for API
333
+ """
334
+ try:
335
+ data = _get_json_standard(url="https://4.ident.me/json", timeout=timeout)
336
+ result = {"status": True, "data": {"ip": data["ip"], "api": "ident.me"}}
337
+ if geo:
338
+ geo_data = {
339
+ "city": data.get("city"),
340
+ "region": None,
341
+ "country": data.get("country"),
342
+ "country_code": data.get("cc"),
343
+ "latitude": data.get("latitude"),
344
+ "longitude": data.get("longitude"),
345
+ "organization": data.get("aso"),
346
+ "timezone": data.get("tz")
347
+ }
348
+ result["data"].update(geo_data)
349
+ return result
350
+ except Exception as e:
351
+ return {"status": False, "error": str(e)}
352
+
353
+
354
+ def _tnedi_me_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
355
+ =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
356
+ """
357
+ Get public IP and geolocation using tnedi.me.
358
+
359
+ :param geo: geolocation flag
360
+ :param timeout: timeout value for API
361
+ """
362
+ try:
363
+ data = _get_json_standard(url="https://4.tnedi.me/json", timeout=timeout)
364
+ result = {"status": True, "data": {"ip": data["ip"], "api": "tnedi.me"}}
365
+ if geo:
366
+ geo_data = {
367
+ "city": data.get("city"),
368
+ "region": None,
369
+ "country": data.get("country"),
370
+ "country_code": data.get("cc"),
371
+ "latitude": data.get("latitude"),
372
+ "longitude": data.get("longitude"),
373
+ "organization": data.get("aso"),
374
+ "timezone": data.get("tz")
375
+ }
376
+ result["data"].update(geo_data)
377
+ return result
378
+ except Exception as e:
379
+ return {"status": False, "error": str(e)}
380
+
381
+
382
+ def _myip_la_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
383
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
384
+ """
385
+ Get public IP and geolocation using myip.la.
386
+
387
+ :param geo: geolocation flag
388
+ :param timeout: timeout value for API
389
+ """
390
+ try:
391
+ data = _get_json_ipv4_forced(url="https://api.myip.la/en?json", timeout=timeout)
392
+ result = {"status": True, "data": {"ip": data["ip"], "api": "myip.la"}}
393
+ if geo:
394
+ loc = data.get("location", {})
395
+ geo_data = {
396
+ "city": loc.get("city"),
397
+ "region": loc.get("province"),
398
+ "country": loc.get("country_name"),
399
+ "country_code": loc.get("country_code"),
400
+ "latitude": float(loc.get("latitude")) if loc.get("latitude") else None,
401
+ "longitude": float(loc.get("longitude")) if loc.get("longitude") else None,
402
+ "organization": None,
403
+ "timezone": None
404
+ }
405
+ result["data"].update(geo_data)
406
+ return result
407
+ except Exception as e:
408
+ return {"status": False, "error": str(e)}
409
+
410
+
411
+ def _freeipapi_com_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]=5
412
+ ) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
413
+ """
414
+ Get public IP and geolocation using freeipapi.com.
415
+
416
+ :param geo: geolocation flag
417
+ :param timeout: timeout value for API
418
+ """
419
+ try:
420
+ data = _get_json_ipv4_forced(url="https://freeipapi.com/api/json", timeout=timeout)
421
+ result = {"status": True, "data": {"ip": data["ipAddress"], "api": "freeipapi.com"}}
422
+ if geo:
423
+ geo_data = {
424
+ "city": data.get("cityName"),
425
+ "region": data.get("regionName"),
426
+ "country": data.get("countryName"),
427
+ "country_code": data.get("countryCode"),
428
+ "latitude": data.get("latitude"),
429
+ "longitude": data.get("longitude"),
430
+ "organization": None,
431
+ "timezone": data.get("timeZone")
432
+ }
433
+ result["data"].update(geo_data)
434
+ return result
435
+ except Exception as e:
436
+ return {"status": False, "error": str(e)}
437
+
438
+
439
+ IPV4_API_MAP = {
440
+ IPv4API.IFCONFIG_CO: {
441
+ "thread_safe": False,
442
+ "geo": True,
443
+ "function": _ifconfig_co_ipv4
444
+ },
445
+ IPv4API.IDENT_ME: {
446
+ "thread_safe": True,
447
+ "geo": True,
448
+ "function": _ident_me_ipv4
449
+ },
450
+ IPv4API.TNEDI_ME: {
451
+ "thread_safe": True,
452
+ "geo": True,
453
+ "function": _tnedi_me_ipv4
454
+ },
455
+ IPv4API.IP_SB: {
456
+ "thread_safe": True,
457
+ "geo": True,
458
+ "function": _ip_sb_ipv4
459
+ },
460
+ IPv4API.IPLEAK_NET: {
461
+ "thread_safe": True,
462
+ "geo": True,
463
+ "function": _ipleak_net_ipv4
464
+ },
465
+ IPv4API.MY_IP_IO: {
466
+ "thread_safe": True,
467
+ "geo": True,
468
+ "function": _my_ip_io_ipv4
469
+ },
470
+ IPv4API.IP_API_COM: {
471
+ "thread_safe": False,
472
+ "geo": True,
473
+ "function": _ip_api_com_ipv4
474
+ },
475
+ IPv4API.IPINFO_IO: {
476
+ "thread_safe": False,
477
+ "geo": True,
478
+ "function": _ipinfo_io_ipv4
479
+ },
480
+ IPv4API.IPAPI_CO: {
481
+ "thread_safe": False,
482
+ "geo": True,
483
+ "function": _ipapi_co_ipv4
484
+ },
485
+ IPv4API.REALLYFREEGEOIP_ORG: {
486
+ "thread_safe": False,
487
+ "geo": True,
488
+ "function": _reallyfreegeoip_org_ipv4
489
+ },
490
+ IPv4API.FREEIPAPI_COM: {
491
+ "thread_safe": False,
492
+ "geo": True,
493
+ "function": _freeipapi_com_ipv4,
494
+ },
495
+ IPv4API.MYIP_LA: {
496
+ "thread_safe": False,
497
+ "geo": True,
498
+ "function": _myip_la_ipv4,
499
+ },
500
+ }
501
+
502
+
503
+ def get_public_ipv4(api: IPv4API=IPv4API.AUTO_SAFE, geo: bool=False,
504
+ timeout: Union[float, Tuple[float, float]]=5,
505
+ max_retries: int = 0,
506
+ retry_delay: float = 1.0) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
507
+ """
508
+ Get public IPv4 and geolocation info based on the selected API.
509
+
510
+ :param api: public IPv4 API
511
+ :param geo: geolocation flag
512
+ :param timeout: timeout value for API
513
+ :param max_retries: number of retries
514
+ :param retry_delay: delay between retries (in seconds)
515
+ """
516
+ if api in [IPv4API.AUTO, IPv4API.AUTO_SAFE]:
517
+ for _, api_data in IPV4_API_MAP.items():
518
+ if api == IPv4API.AUTO_SAFE and not api_data["thread_safe"]:
519
+ continue
520
+ func = api_data["function"]
521
+ result = _attempt_with_retries(
522
+ func=func,
523
+ max_retries=max_retries,
524
+ retry_delay=retry_delay,
525
+ geo=geo,
526
+ timeout=timeout)
527
+ if result["status"]:
528
+ return result
529
+ return {"status": False, "error": "All attempts failed."}
530
+ else:
531
+ api_data = IPV4_API_MAP.get(api)
532
+ if api_data:
533
+ func = api_data["function"]
534
+ return _attempt_with_retries(
535
+ func=func,
536
+ max_retries=max_retries,
537
+ retry_delay=retry_delay,
538
+ geo=geo,
539
+ timeout=timeout)
540
+ return {"status": False, "error": "Unsupported API: {api}".format(api=api)}
ipspot/params.py CHANGED
@@ -2,7 +2,7 @@
2
2
  """ipspot params."""
3
3
  from enum import Enum
4
4
 
5
- IPSPOT_VERSION = "0.2"
5
+ IPSPOT_VERSION = "0.4"
6
6
 
7
7
  IPSPOT_OVERVIEW = '''
8
8
  IPSpot is a Python library for retrieving the current system's IP address and location information.
@@ -22,9 +22,19 @@ class IPv4API(Enum):
22
22
  """Public IPv4 API enum."""
23
23
 
24
24
  AUTO = "auto"
25
- IPAPI = "ipapi"
26
- IPINFO = "ipinfo"
27
- IPSB = "ipsb"
25
+ AUTO_SAFE = "auto-safe"
26
+ IP_API_COM = "ip-api.com"
27
+ IPAPI_CO = "ipapi.co"
28
+ IPINFO_IO = "ipinfo.io"
29
+ IP_SB = "ip.sb"
30
+ IDENT_ME = "ident.me"
31
+ TNEDI_ME = "tnedi.me"
32
+ IPLEAK_NET = "ipleak.net"
33
+ MY_IP_IO = "my-ip.io"
34
+ IFCONFIG_CO = "ifconfig.co"
35
+ REALLYFREEGEOIP_ORG = "reallyfreegeoip.org"
36
+ MYIP_LA = "myip.la"
37
+ FREEIPAPI_COM = "freeipapi.com"
28
38
 
29
39
 
30
40
  PARAMETERS_NAME_MAP = {
ipspot/utils.py ADDED
@@ -0,0 +1,69 @@
1
+ # -*- coding: utf-8 -*-
2
+ """ipspot utils."""
3
+ import time
4
+ import ipaddress
5
+ import requests
6
+ from typing import Callable, Dict
7
+ from typing import Union, Tuple, Any
8
+ from .params import REQUEST_HEADERS
9
+
10
+
11
+ def _attempt_with_retries(
12
+ func: Callable,
13
+ max_retries: int,
14
+ retry_delay: float, **kwargs: dict) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
15
+ """
16
+ Attempt a function call with retries and delay.
17
+
18
+ :param func: function to execute
19
+ :param max_retries: number of retries
20
+ :param retry_delay: delay between retries (in seconds)
21
+ :param kwargs: keyword arguments to pass to the function
22
+ """
23
+ max_retries = max(0, max_retries)
24
+ result = {"status": False, "error": ""}
25
+ for attempt in range(max_retries + 1):
26
+ result = func(**kwargs)
27
+ if result["status"]:
28
+ break
29
+ time.sleep(retry_delay)
30
+ return result
31
+
32
+
33
+ def is_loopback(ip: str) -> bool:
34
+ """
35
+ Check if the given input IP is a loopback address.
36
+
37
+ :param ip: input IP
38
+ """
39
+ try:
40
+ ip_object = ipaddress.ip_address(ip)
41
+ return ip_object.is_loopback
42
+ except Exception:
43
+ return False
44
+
45
+
46
+ def _filter_parameter(parameter: Any) -> Any:
47
+ """
48
+ Filter input parameter.
49
+
50
+ :param parameter: input parameter
51
+ """
52
+ if parameter is None:
53
+ return "N/A"
54
+ if isinstance(parameter, str) and len(parameter.strip()) == 0:
55
+ return "N/A"
56
+ return parameter
57
+
58
+
59
+ def _get_json_standard(url: str, timeout: Union[float, Tuple[float, float]]) -> dict:
60
+ """
61
+ Send standard GET request that returns JSON response.
62
+
63
+ :param url: API url
64
+ :param timeout: timeout value for API
65
+ """
66
+ with requests.Session() as session:
67
+ response = session.get(url, headers=REQUEST_HEADERS, timeout=timeout)
68
+ response.raise_for_status()
69
+ return response.json()
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipspot
3
- Version: 0.2
3
+ Version: 0.4
4
4
  Summary: IPSpot: A Python Tool to Fetch the System's IP Address
5
5
  Home-page: https://github.com/openscilab/ipspot
6
- Download-URL: https://github.com/openscilab/ipspot/tarball/v0.2
6
+ Download-URL: https://github.com/openscilab/ipspot/tarball/v0.4
7
7
  Author: IPSpot Development Team
8
8
  Author-email: ipspot@openscilab.com
9
9
  License: MIT
@@ -102,13 +102,13 @@ Dynamic: summary
102
102
  ## Installation
103
103
 
104
104
  ### Source Code
105
- - Download [Version 0.2](https://github.com/openscilab/ipspot/archive/v0.2.zip) or [Latest Source](https://github.com/openscilab/ipspot/archive/dev.zip)
105
+ - Download [Version 0.4](https://github.com/openscilab/ipspot/archive/v0.4.zip) or [Latest Source](https://github.com/openscilab/ipspot/archive/dev.zip)
106
106
  - `pip install .`
107
107
 
108
108
  ### PyPI
109
109
 
110
110
  - Check [Python Packaging User Guide](https://packaging.python.org/installing/)
111
- - `pip install ipspot==0.2`
111
+ - `pip install ipspot==0.4`
112
112
 
113
113
 
114
114
  ## Usage
@@ -119,9 +119,11 @@ Dynamic: summary
119
119
 
120
120
  ```pycon
121
121
  >>> from ipspot import get_public_ipv4, IPv4API
122
- >>> get_public_ipv4(api=IPv4API.IPAPI)
122
+ >>> get_public_ipv4(api=IPv4API.IP_API_COM)
123
123
  {'status': True, 'data': {'ip': 'xx.xx.xx.xx', 'api': 'ip-api.com'}}
124
- >>> get_public_ipv4(api=IPv4API.IPAPI, geo=True, timeout=10)
124
+ >>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10)
125
+ {'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}
126
+ >>> get_public_ipv4(api=IPv4API.IP_API_COM, geo=True, timeout=10, max_retries=5, retry_delay=4)
125
127
  {'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}
126
128
  ```
127
129
 
@@ -142,7 +144,7 @@ Dynamic: summary
142
144
  ```console
143
145
  > ipspot --version
144
146
 
145
- 0.2
147
+ 0.4
146
148
  ```
147
149
 
148
150
  #### Info
@@ -157,11 +159,11 @@ Dynamic: summary
157
159
  |___||_| |____/ | .__/ \___/ \__|
158
160
  |_|
159
161
 
160
- __ __ ___ ____
161
- \ \ / / _ / _ \ |___ \
162
- \ \ / / (_)| | | | __) |
163
- \ V / _ | |_| | _ / __/
164
- \_/ (_) \___/ (_)|_____|
162
+ __ __ ___ _ _
163
+ \ \ / / _ / _ \ | || |
164
+ \ \ / / (_)| | | | | || |_
165
+ \ V / _ | |_| | _ |__ _|
166
+ \_/ (_) \___/ (_) |_|
165
167
 
166
168
 
167
169
 
@@ -197,12 +199,12 @@ Public IP and Location Info:
197
199
 
198
200
  #### IPv4 API
199
201
 
200
- ℹ️ `ipv4-api` valid choices: [`auto`, `ipapi`, `ipinfo`, `ipsb`]
202
+ ℹ️ `ipv4-api` valid choices: [`auto-safe`, `auto`, `ip-api.com`, `ipinfo.io`, `ip.sb`, `ident.me`, `tnedi.me`, `ipapi.co`, `ipleak.net`, `my-ip.io`, `ifconfig.co`, `reallyfreegeoip.org`, `freeipapi.com`, `myip.la`]
201
203
 
202
- ℹ️ The default value: `auto`
204
+ ℹ️ The default value: `auto-safe`
203
205
 
204
206
  ```console
205
- > ipspot --ipv4-api="ipinfo"
207
+ > ipspot --ipv4-api="ipinfo.io"
206
208
  Private IP:
207
209
 
208
210
  10.36.18.154
@@ -267,6 +269,46 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
267
269
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
268
270
 
269
271
  ## [Unreleased]
272
+ ## [0.4] - 2025-06-09
273
+ ### Added
274
+ - Support [ipapi.co](https://ipapi.co/json/)
275
+ - Support [ipleak.net](https://ipleak.net/json/)
276
+ - Support [my-ip.io](https://www.my-ip.io/)
277
+ - Support [ifconfig.co](https://ifconfig.co/json)
278
+ - Support [reallyfreegeoip.org](https://reallyfreegeoip.org/json/)
279
+ - Support [myip.la](https://api.myip.la/en?json)
280
+ - Support [freeipapi.com](https://freeipapi.com/api/json/)
281
+ - `AUTO_SAFE` mode
282
+ - `_get_json_standard` function
283
+ - `_get_json_ipv4_forced` function
284
+ - `--max-retries` argument
285
+ - `--retry-delay` argument
286
+ ### Changed
287
+ - `IPv4API.IPAPI` renamed to `IPv4API.IP_API_COM`
288
+ - `IPv4API.IPINFO` renamed to `IPv4API.IPINFO_IO`
289
+ - `IPv4API.IPSB` renamed to `IPv4API.IP_SB`
290
+ - `IPv4API.IDENTME` renamed to `IPv4API.IDENT_ME`
291
+ - `IPv4API.TNEDIME` renamed to `IPv4API.TNEDI_ME`
292
+ - `get_public_ipv4` function modified
293
+ - `filter_parameter` function renamed to `_filter_parameter`
294
+ - `README.md` updated
295
+ ## [0.3] - 2025-05-19
296
+ ### Added
297
+ - `is_ipv4` function
298
+ - `is_loopback` function
299
+ - `IPv4HTTPAdapter` class
300
+ - Support [ident.me](https://ident.me/json)
301
+ - Support [tnedi.me](https://tnedi.me/json)
302
+ ### Changed
303
+ - `get_private_ipv4` function modified
304
+ - `get_public_ipv4` function modified
305
+ - `_ipsb_ipv4` function modified
306
+ - `_ipapi_ipv4` function modified
307
+ - `_ipinfo_ipv4` function modified
308
+ - `functions.py` renamed to `utils.py`
309
+ - CLI functions moved to `cli.py`
310
+ - IPv4 functions moved to `ipv4.py`
311
+ - Test system modified
270
312
  ## [0.2] - 2025-05-04
271
313
  ### Added
272
314
  - Support [ip.sb](https://api.ip.sb/geoip)
@@ -286,7 +328,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
286
328
  - `--no-geo` argument
287
329
  - Logo
288
330
 
289
- [Unreleased]: https://github.com/openscilab/ipspot/compare/v0.2...dev
331
+ [Unreleased]: https://github.com/openscilab/ipspot/compare/v0.4...dev
332
+ [0.4]: https://github.com/openscilab/ipspot/compare/v0.3...v0.4
333
+ [0.3]: https://github.com/openscilab/ipspot/compare/v0.2...v0.3
290
334
  [0.2]: https://github.com/openscilab/ipspot/compare/v0.1...v0.2
291
335
  [0.1]: https://github.com/openscilab/ipspot/compare/3216fb7...v0.1
292
336
 
@@ -0,0 +1,13 @@
1
+ ipspot/__init__.py,sha256=e_1FqqNDD9eoDVw9eKL_1pS7du-Sei3lm6A4UFjYiUo,211
2
+ ipspot/__main__.py,sha256=xKHY_tc94SWktkIHV0O9YADAHPZvT8yXOFSm_L2yMno,105
3
+ ipspot/cli.py,sha256=M23l9ftqKbdDGhb906q4ywJmcjkHW3VWijyXj76APC4,3222
4
+ ipspot/ipv4.py,sha256=zGFvzD2GeM2lFxMWc6lcCphBaj5qY0kxiHE0FBhDoTc,19998
5
+ ipspot/params.py,sha256=hdArSL10EEq4vtfxA51ftCkn5WfsAzsxdoX1T11yCdQ,1419
6
+ ipspot/utils.py,sha256=YtkyWGljpFc72rSGljsARjlVrF9hkE7Gl78_5uex1yI,1870
7
+ ipspot-0.4.dist-info/licenses/AUTHORS.md,sha256=5ZvxP1KnuVkurB4psQDSqnqiAyn44q1aeiXUsnYgps4,411
8
+ ipspot-0.4.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
9
+ ipspot-0.4.dist-info/METADATA,sha256=8D5MqjjxrCDU60BDPikNoX75iE5bMKVMQX_kFw03WiU,10875
10
+ ipspot-0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ ipspot-0.4.dist-info/entry_points.txt,sha256=DJVLepYr8H3UcvWekU5Jy-tcr_xmWrphzgWGNOd3hlk,43
12
+ ipspot-0.4.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
13
+ ipspot-0.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ipspot = ipspot.cli:main
@@ -1,6 +1,8 @@
1
1
  # Core Developers
2
2
  ----------
3
3
  - Sepand Haghighi - Open Science Laboratory ([Github](https://github.com/sepandhaghighi)) **
4
+ - Sadra Sabouri - Open Science Laboratory ([Github](https://github.com/sadrasabouri))
5
+ - AmirHosein Rostami - Open Science Laboratory ([Github](https://github.com/AHReccese))
4
6
 
5
7
  ** **Maintainer**
6
8
 
ipspot/functions.py DELETED
@@ -1,218 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """ipspot functions."""
3
- import argparse
4
- import socket
5
- from typing import Union, Dict, Tuple, Any
6
- import requests
7
- from art import tprint
8
- from .params import REQUEST_HEADERS, IPv4API, PARAMETERS_NAME_MAP
9
- from .params import IPSPOT_OVERVIEW, IPSPOT_REPO, IPSPOT_VERSION
10
-
11
-
12
- def ipspot_info() -> None: # pragma: no cover
13
- """Print ipspot details."""
14
- tprint("IPSpot")
15
- tprint("V:" + IPSPOT_VERSION)
16
- print(IPSPOT_OVERVIEW)
17
- print("Repo : " + IPSPOT_REPO)
18
-
19
-
20
- def get_private_ipv4() -> Dict[str, Union[bool, Dict[str, str], str]]:
21
- """Retrieve the private IPv4 address."""
22
- try:
23
- hostname = socket.gethostname()
24
- private_ip = socket.gethostbyname(hostname)
25
- return {"status": True, "data": {"ip": private_ip}}
26
- except Exception as e:
27
- return {"status": False, "error": str(e)}
28
-
29
-
30
- def _ipsb_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
31
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
32
- """
33
- Get public IP and geolocation using ip.sb.
34
-
35
- :param geo: geolocation flag
36
- :param timeout: timeout value for API
37
- """
38
- try:
39
- response = requests.get("https://api.ip.sb/geoip", headers=REQUEST_HEADERS, timeout=timeout)
40
- response.raise_for_status()
41
- data = response.json()
42
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "ip.sb"}}
43
- if geo:
44
- geo_data = {
45
- "city": data.get("city"),
46
- "region": data.get("region"),
47
- "country": data.get("country"),
48
- "country_code": data.get("country_code"),
49
- "latitude": data.get("latitude"),
50
- "longitude": data.get("longitude"),
51
- "organization": data.get("organization"),
52
- "timezone": data.get("timezone")
53
- }
54
- result["data"].update(geo_data)
55
- return result
56
- except Exception as e:
57
- return {"status": False, "error": str(e)}
58
-
59
-
60
- def _ipapi_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
61
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
62
- """
63
- Get public IP and geolocation using ip-api.com.
64
-
65
- :param geo: geolocation flag
66
- :param timeout: timeout value for API
67
- """
68
- try:
69
- response = requests.get("http://ip-api.com/json/", headers=REQUEST_HEADERS, timeout=timeout)
70
- response.raise_for_status()
71
- data = response.json()
72
-
73
- if data.get("status") != "success":
74
- return {"status": False, "error": "ip-api lookup failed"}
75
- result = {"status": True, "data": {"ip": data.get("query"), "api": "ip-api.com"}}
76
- if geo:
77
- geo_data = {
78
- "city": data.get("city"),
79
- "region": data.get("regionName"),
80
- "country": data.get("country"),
81
- "country_code": data.get("countryCode"),
82
- "latitude": data.get("lat"),
83
- "longitude": data.get("lon"),
84
- "organization": data.get("org"),
85
- "timezone": data.get("timezone")
86
- }
87
- result["data"].update(geo_data)
88
- return result
89
- except Exception as e:
90
- return {"status": False, "error": str(e)}
91
-
92
-
93
- def _ipinfo_ipv4(geo: bool=False, timeout: Union[float, Tuple[float, float]]
94
- =5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
95
- """
96
- Get public IP and geolocation using ipinfo.io.
97
-
98
- :param geo: geolocation flag
99
- :param timeout: timeout value for API
100
- """
101
- try:
102
- response = requests.get("https://ipinfo.io/json", headers=REQUEST_HEADERS, timeout=timeout)
103
- response.raise_for_status()
104
- data = response.json()
105
- result = {"status": True, "data": {"ip": data.get("ip"), "api": "ipinfo.io"}}
106
- if geo:
107
- loc = data.get("loc", "").split(",")
108
- geo_data = {
109
- "city": data.get("city"),
110
- "region": data.get("region"),
111
- "country": None,
112
- "country_code": data.get("country"),
113
- "latitude": float(loc[0]) if len(loc) == 2 else None,
114
- "longitude": float(loc[1]) if len(loc) == 2 else None,
115
- "organization": data.get("org"),
116
- "timezone": data.get("timezone")
117
- }
118
- result["data"].update(geo_data)
119
- return result
120
- except Exception as e:
121
- return {"status": False, "error": str(e)}
122
-
123
-
124
- def get_public_ipv4(api: IPv4API=IPv4API.AUTO, geo: bool=False,
125
- timeout: Union[float, Tuple[float, float]]=5) -> Dict[str, Union[bool, Dict[str, Union[str, float]], str]]:
126
- """
127
- Get public IPv4 and geolocation info based on the selected API.
128
-
129
- :param api: public IPv4 API
130
- :param geo: geolocation flag
131
- :param timeout: timeout value for API
132
- """
133
- api_map = {
134
- IPv4API.IPAPI: _ipapi_ipv4,
135
- IPv4API.IPINFO: _ipinfo_ipv4,
136
- IPv4API.IPSB: _ipsb_ipv4
137
- }
138
-
139
- if api == IPv4API.AUTO:
140
- for _, func in api_map.items():
141
- result = func(geo=geo, timeout=timeout)
142
- if result["status"]:
143
- return result
144
- return {"status": False, "error": "All attempts failed."}
145
- else:
146
- func = api_map.get(api)
147
- if func:
148
- return func(geo=geo, timeout=timeout)
149
- return {"status": False, "error": "Unsupported API: {api}".format(api=api)}
150
-
151
-
152
- def filter_parameter(parameter: Any) -> Any:
153
- """
154
- Filter input parameter.
155
-
156
- :param parameter: input parameter
157
- """
158
- if parameter is None:
159
- return "N/A"
160
- if isinstance(parameter, str) and len(parameter.strip()) == 0:
161
- return "N/A"
162
- return parameter
163
-
164
-
165
- def display_ip_info(ipv4_api: IPv4API = IPv4API.AUTO, geo: bool=False,
166
- timeout: Union[float, Tuple[float, float]]=5) -> None: # pragma: no cover
167
- """
168
- Print collected IP and location data.
169
-
170
- :param ipv4_api: public IPv4 API
171
- :param geo: geolocation flag
172
- :param timeout: timeout value for API
173
- """
174
- private_result = get_private_ipv4()
175
- print("Private IP:\n")
176
- print(" IP: {private_result[data][ip]}".format(private_result=private_result) if private_result["status"]
177
- else " Error: {private_result[error]}".format(private_result=private_result))
178
-
179
- public_title = "\nPublic IP"
180
- if geo:
181
- public_title += " and Location Info"
182
- public_title += ":\n"
183
- print(public_title)
184
- public_result = get_public_ipv4(ipv4_api, geo=geo, timeout=timeout)
185
- if public_result["status"]:
186
- for name, parameter in sorted(public_result["data"].items()):
187
- print(
188
- " {name}: {parameter}".format(
189
- name=PARAMETERS_NAME_MAP[name],
190
- parameter=filter_parameter(parameter)))
191
- else:
192
- print(" Error: {public_result[error]}".format(public_result=public_result))
193
-
194
-
195
- def main() -> None: # pragma: no cover
196
- """CLI main function."""
197
- parser = argparse.ArgumentParser()
198
- parser.add_argument(
199
- '--ipv4-api',
200
- help='public IPv4 API',
201
- type=str.lower,
202
- choices=[
203
- x.value for x in IPv4API],
204
- default=IPv4API.AUTO.value)
205
- parser.add_argument('--info', help='info', nargs="?", const=1)
206
- parser.add_argument('--version', help='version', nargs="?", const=1)
207
- parser.add_argument('--no-geo', help='no geolocation data', nargs="?", const=1, default=False)
208
- parser.add_argument('--timeout', help='timeout for the API request', type=float, default=5.0)
209
-
210
- args = parser.parse_args()
211
- if args.version:
212
- print(IPSPOT_VERSION)
213
- elif args.info:
214
- ipspot_info()
215
- else:
216
- ipv4_api = IPv4API(args.ipv4_api)
217
- geo = not args.no_geo
218
- display_ip_info(ipv4_api=ipv4_api, geo=geo, timeout=args.timeout)
@@ -1,11 +0,0 @@
1
- ipspot/__init__.py,sha256=cxty-JGyo_sLeQd7bHrXUoEabl3QhLtgA9UtLcStp6o,176
2
- ipspot/__main__.py,sha256=17x2Q5vGORstO8mZ8B9aDixcOK5Gl71Ej5fC5P1EBKY,111
3
- ipspot/functions.py,sha256=JNeLJw-zIZzQQhul1SHPBzBNiFtoJvjzMUIqkeJNn3A,7862
4
- ipspot/params.py,sha256=I-waqgQ83px4ybrgpD1MAYFi7Zre1T9GSBN25-eZrVk,1099
5
- ipspot-0.2.dist-info/licenses/AUTHORS.md,sha256=AgFL2paPd70ItL_YK18lWm_NjNWytKZHmR57o-et8kU,236
6
- ipspot-0.2.dist-info/licenses/LICENSE,sha256=0aOd4wzZRoSH_35UZXRHS7alPFTtuFEBJrajLuonEIw,1067
7
- ipspot-0.2.dist-info/METADATA,sha256=yQNY-arJj8IQfVT2vvySrEcytBVzvYCiac5QhLFK1Dk,8798
8
- ipspot-0.2.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
9
- ipspot-0.2.dist-info/entry_points.txt,sha256=SYgm9eGlJY7AGqi4_PmFVXwjEoylPPnAHeAeJPtYdjk,49
10
- ipspot-0.2.dist-info/top_level.txt,sha256=v0WgE1z2iCy_bXU53fVcllwHLTvGNBIvq8u3KPC2_Sc,7
11
- ipspot-0.2.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- ipspot = ipspot.functions:main