diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e6fc16 --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +ipify2 +============ + +An updated unofficial clone of the now-deprecated `ipify` library, allowing you to determine your IPv4 and IPv6 programmatically via [ipify.org](https://www.ipify.org) + +.. image:: https://img.shields.io/pypi/v/ipify2.svg + :alt: ipify2 Release + :target: https://pypi.python.org/pypi/ipify2 + +.. image:: https://img.shields.io/pypi/dm/ipify2.svg + :alt: ipify2 Downloads + :target: https://pypi.python.org/pypi/ipify2 + + +Meta +---- + +### Original + +- Author: Randall Degges +- Email: r@rdegges.com +- Site: http://www.rdegges.com + +### Maintainer + +- Author: Nate Harris +- Email: n8gr8gbln@gmail.com +- Site: https://nateharr.is + + +Purpose +------- + +[ipify.org](https://www.ipify.org) is a reliable IP address lookup service, an easy way to get your public IP address in Python. + +This library will retrieve your public IP address from ipify's API service, and return it as a string. + +Additional features: + +- If a request fails for any reason, it is re-attempted 3 times using an exponential backoff algorithm for maximum effectiveness. +- This library handles exceptions properly, and usage examples below show you how to deal with errors in a foolproof way. +- This library only makes API requests over HTTPS. + + +Installation +------------ + +To install ``ipify2``, simply run: + +```shell +pip install ipify2 +``` + +This will install the latest version of the library automatically. + + +Usage +----- + +Using this library is very simple. Here's a simple example: + +```python +from ipify2 import get_ipv4 + +ip = get_ipv4() +print(ip) # '96.41.136.144' +``` + +```python +from ipify2 import get_ipv6 +ip = get_ipv6() +print(ip) # '2001:0db8:85a3:0000:0000:8a2e:0370:7334' +``` + +### Error Handling +There are several reasons a request fail: +- The ipify service is down +- Your machine is unable to get the request to ipify because of a network error + of some sort (DNS, no internet, etc.). + +To handle these errors, you can do the following: + +```python +from ipify2 import get_universal_ip +from ipify2.exceptions import ConnectionError, ServiceError + +try: + ip = get_universal_ip() +except ConnectionError: + # If you get here, it means you were unable to reach the ipify service, + # most likely because of a network error on your end. +except ServiceError: + # If you get here, it means ipify is having issues, so the request + # couldn't be completed :( +except: + # Something else happened (non-ipify related). Maybe you hit CTRL-C + # while the program was running, the kernel is killing your process, or + # something else all together. +``` + +If you want to simplify the above error handling by catching all errors, you could also do the following: + +```python +from ipify2 import get_universal_ip +from ipify2.exceptions import IpifyException + +try: + ip = get_universal_ip() +except IpifyException: + # If you get here, then some ipify exception occurred. +except: + # If you get here, some non-ipify related exception occurred. +``` + +One thing to keep in mind: regardless of how you decide to handle exceptions, the ipify library will retry any failed requests 3 times before ever raising exceptions -- so if you *do* need to handle exceptions, just remember that retry logic has already been attempted. diff --git a/README.rst b/README.rst deleted file mode 100644 index dd6209e..0000000 --- a/README.rst +++ /dev/null @@ -1,152 +0,0 @@ -**ipify2: This is a maintained, updated fork of the original ipify package** - - -python-ipify -============ - -The official client library for `ipify `_: *A Simple IP -Address API*. - -.. image:: https://img.shields.io/pypi/v/ipify2.svg - :alt: ipify2 Release - :target: https://pypi.python.org/pypi/ipify2 - -.. image:: https://img.shields.io/pypi/dm/ipify2.svg - :alt: ipify2 Downloads - :target: https://pypi.python.org/pypi/ipify2 - - -Meta ----- - -Original - -- Author: Randall Degges -- Email: r@rdegges.com -- Site: http://www.rdegges.com - -Maintainer - -- Author: Nate Harris -- Email: n8gr8gbln@gmail.com -- Site: https://nateharr.is - - -Purpose -------- - -`ipify `_ is the best IP address lookup service on the -internet. It's fast, simple, scalable, open source, and well-funded (*by me!*). - -In short: if you need a way to pragmatically get your public IP address, ipify -is the best possible choice! - -This library will retrieve your public IP address from ipify's API service, and -return it as a string. It can't get any simpler than that. - -This library also has some other nice features you might care about: - -- If a request fails for any reason, it is re-attempted 3 times using an - exponential backoff algorithm for maximum effectiveness. -- This library handles exceptions properly, and usage examples below show you - how to deal with errors in a foolproof way. -- This library only makes API requests over HTTPS. - - -Installation ------------- - -To install ``ipify2``, simply run: - -.. code-block:: console - - $ pip install ipify2 - -This will install the latest version of the library automatically. - - -Usage ------ - -Using this library is very simple. Here's a simple example: - -.. code-block:: python - - >>> from ipify2 import get_ipv4, get_universal_ip - >>> ip = get_ipv4() - >>> ip - u'96.41.136.144' - -Now, in regards to exception handling, there are several ways this can fail: - -- The ipify service is down (*not likely*), or: -- Your machine is unable to get the request to ipify because of a network error - of some sort (DNS, no internet, etc.). - -Here's how you can handle all of these edge cases: - -.. code-block:: python - - from ipify2 import get_ip - from ipify2.exceptions import ConnectionError, ServiceError - - try: - ip = get_ip() - except ConnectionError: - # If you get here, it means you were unable to reach the ipify service, - # most likely because of a network error on your end. - except ServiceError: - # If you get here, it means ipify is having issues, so the request - # couldn't be completed :( - except: - # Something else happened (non-ipify related). Maybe you hit CTRL-C - # while the program was running, the kernel is killing your process, or - # something else all together. - -If you want to simplify the above error handling, you could also do the -following (*it will catch any sort of ipify related errors regardless of what -type they may be*): - -.. code-block:: python - - from ipify2 import get_ipv4 - from ipify2.exceptions import IpifyException - - try: - ip = get_ipv4() - except IpifyException: - # If you get here, then some ipify exception occurred. - except: - # If you get here, some non-ipify related exception occurred. - -One thing to keep in mind: regardless of how you decide to handle exceptions, -the ipify library will retry any failed requests 3 times before ever raising -exceptions -- so if you *do* need to handle exceptions, just remember that retry -logic has already been attempted. - - -Contributing ------------- - -This project is only possible due to the amazing contributors who work on it! - -If you'd like to improve this library, please send me a pull request! I'm happy -to review and merge pull requests. - -The standard contribution workflow should look something like this: - -- Fork this project on Github. -- Make some changes in the master branch (*this project is simple, so no need to - complicate things*). -- Send a pull request when ready. - -Also, if you're making changes, please write tests for your changes -- this -project has a full test suite you can easily modify / test. - -To run the test suite, you can use the following commands: - -.. code-block:: console - - $ pip install -e . - $ pip install -r requirements.txt - $ python setup.py test diff --git a/ipify2/__info__.py b/ipify2/__info__.py index f646d40..5401cab 100644 --- a/ipify2/__info__.py +++ b/ipify2/__info__.py @@ -1,4 +1,4 @@ -__version__ = '1.0.2' +__version__ = '1.1.0' __title__ = "ipify2" __author__ = 'Nate Harris' diff --git a/ipify2/__init__.py b/ipify2/__init__.py index a9a9b36..08e31ad 100644 --- a/ipify2/__init__.py +++ b/ipify2/__init__.py @@ -25,4 +25,4 @@ """ -from .ipify import get_ipv4, get_universal_ip +from .ipify import get_ipv4, get_ipv6, get_universal_ip diff --git a/ipify2/exceptions.py b/ipify2/exceptions.py index 84d702b..09aed2b 100644 --- a/ipify2/exceptions.py +++ b/ipify2/exceptions.py @@ -9,14 +9,14 @@ class IpifyException(Exception): """ There was an ambiguous exception that occurred while attempting to fetch - your machine's public IP address from the ipify2 service. + your machine's public IP address from the ipify service. """ pass class ServiceError(IpifyException): """ - The request failed because the ipify2 service is currently down or + The request failed because the ipify service is currently down or experiencing issues. """ pass @@ -24,7 +24,7 @@ class ServiceError(IpifyException): class ConnectionError(IpifyException): """ - The request failed because it wasn't able to reach the ipify2 service. This + The request failed because it wasn't able to reach the ipify service. This is most likely due to a networking error of some sort. """ pass diff --git a/ipify2/ipify.py b/ipify2/ipify.py index d77447e..4721c85 100644 --- a/ipify2/ipify.py +++ b/ipify2/ipify.py @@ -1,11 +1,10 @@ """ -ipify2.ipify2 +ipify2.ipify ~~~~~~~~~~~ The module holds the main ipify2 library implementation. """ - from backoff import expo, on_exception from requests import get from requests.exceptions import RequestException @@ -13,12 +12,15 @@ from .exceptions import ConnectionError, ServiceError from .settings import MAX_TRIES, USER_AGENT +IPV4_URL = "https://api.ipify.org" +IPV6_URL = "https://api64.ipify.org" + @on_exception(expo, RequestException, max_tries=MAX_TRIES) def _get_ip_resp(api_url: str): """ Internal function which attempts to retrieve this machine's public IP - address from the ipify2 service (https://www.ipify.org). + address from the ipify service (https://www.ipify.org). :rtype: obj :returns: The response object from the HTTP request. @@ -35,43 +37,60 @@ def _get_ip_resp(api_url: str): def get_ipv4(): """ - Query the ipify2 service (https://www.ipify.org) to retrieve this machine's + Query the ipify service (https://www.ipify.org) to retrieve this machine's public IPv4 address. :rtype: string :returns: The public IPv4 address of this machine as a string. - :raises: ConnectionError if the request couldn't reach the ipify2 service, + :raises: ConnectionError if the request couldn't reach the ipify service, or ServiceError if there was a problem getting the IPv4 address from - ipify2's service. + ipify's service. """ try: - resp = _get_ip_resp(api_url="https://api.ipify.org") + resp = _get_ip_resp(api_url=IPV4_URL) except RequestException: - raise ConnectionError("The request failed because it wasn't able to reach the ipify2 service. This is most likely due to a networking error of some sort.") + raise ConnectionError("The request failed because it wasn't able to reach the ipify service. This is most " + "likely due to a networking error of some sort.") if resp.status_code != 200: - raise ServiceError('Received an invalid status code from ipify2:' + str(resp.status_code) + '. The service might be experiencing issues.') + raise ServiceError(f'Received an invalid status code from ipify: {resp.status_code}. The service might be ' + f'experiencing issues.') return resp.text def get_universal_ip(): """ - Query the ipify2 service (https://www.ipify.org) to retrieve this machine's + Query the ipify service (https://www.ipify.org) to retrieve this machine's public IPv4/IPv6 address. :rtype: string :returns: The public IPv4/IPv6 address of this machine as a string. - :raises: ConnectionError if the request couldn't reach the ipify2 service, + :raises: ConnectionError if the request couldn't reach the ipify service, or ServiceError if there was a problem getting the IPv4/IPv6 address from - ipify2's service. + ipify's service. """ try: - resp = _get_ip_resp(api_url="https://api64.ipify.org") + resp = _get_ip_resp(api_url=IPV6_URL) except RequestException: - raise ConnectionError("The request failed because it wasn't able to reach the ipify2 service. This is most likely due to a networking error of some sort.") + raise ConnectionError("The request failed because it wasn't able to reach the ipify service. This is most " + "likely due to a networking error of some sort.") if resp.status_code != 200: - raise ServiceError('Received an invalid status code from ipify2:' + str(resp.status_code) + '. The service might be experiencing issues.') + raise ServiceError(f'Received an invalid status code from ipify: {resp.status_code}. The service might be ' + f'experiencing issues.') return resp.text + + +def get_ipv6(): + """ + See :func:`get_universal_ip`. + + :rtype: string + :returns: The public IPv4/IPv6 address of this machine as a string. + :raises: ConnectionError if the request couldn't reach the ipify service, + or ServiceError if there was a problem getting the IPv4/IPv6 address from + ipify's service. + """ + return get_universal_ip() diff --git a/setup.py b/setup.py index 1050224..832fdbb 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ import ipify2.__info__ as package_info -with open("README.rst", "r") as fh: +with open("README.md", "r") as fh: long_description = fh.read() with open("requirements.txt", 'r') as fh: @@ -46,7 +46,7 @@ def run(self): packages=[package_info.__title__], # Choose the same as "name" version=package_info.__version__, # Start with a small number and increase it with every change you make license=package_info.__license__, - description="Interact with PocketCast's unofficial API", # Give a short description about your library + description="Get IP address information via ipify.org", # Give a short description about your library long_description=long_description, long_description_content_type="text/markdown", author=package_info.__author__, # Type in your name @@ -60,7 +60,7 @@ def run(self): cmdclass={ 'test': TestCommand, }, - keywords=['Python', 'API', 'client', 'ipify2', 'ip', 'address', 'public', 'ipv4', 'ipv6', 'service'], + keywords=['Python', 'API', 'client', 'ipify', 'ipify2', 'ip', 'address', 'public', 'ipv4', 'ipv6', 'service'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -83,4 +83,4 @@ def run(self): 'Topic :: Utilities', ], python_requires='>=3.0' -) \ No newline at end of file +)