GithubHelp home page GithubHelp logo

autocracy / python-ipy Goto Github PK

View Code? Open in Web Editor NEW
560.0 36.0 176.0 319 KB

IPy are a Python class and tools for handling of IPv4 and IPv6 addresses and networks. It is similar to Net::IP Perl module.

License: Other

Python 99.46% Makefile 0.54%

python-ipy's Introduction

IPy - class and tools for handling of IPv4 and IPv6 addresses and networks.

Website: https://github.com/autocracy/python-ipy/

Presentation of the API

The IP class allows a comfortable parsing and handling for most notations in use for IPv4 and IPv6 addresses and networks. It was greatly inspired by RIPE's Perl module NET::IP's interface but doesn't share the implementation. It doesn't share non-CIDR netmasks, so funky stuff like a netmask of 0xffffff0f can't be done here.

>>> from IPy import IP
>>> ip = IP('127.0.0.0/30')
>>> for x in ip:
...  print(x)
...
127.0.0.0
127.0.0.1
127.0.0.2
127.0.0.3
>>> ip2 = IP('0x7f000000/30')
>>> ip == ip2
1
>>> ip.reverseNames()
['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.', '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
>>> ip.reverseName()
'0-3.0.0.127.in-addr.arpa.'
>>> ip.iptype()
'LOOPBACK'

Supports most IP address formats

It can detect about a dozen different ways of expressing IP addresses and networks, parse them and distinguish between IPv4 and IPv6 addresses:

>>> IP('10.0.0.0/8').version()
4
>>> IP('::1').version()
6

IPv4 addresses

>>> print(IP(0x7f000001))
127.0.0.1
>>> print(IP('0x7f000001'))
127.0.0.1
>>> print(IP('127.0.0.1'))
127.0.0.1
>>> print(IP('10'))
10.0.0.0

IPv6 addresses

>>> print(IP('1080:0:0:0:8:800:200C:417A'))
1080::8:800:200c:417a
>>> print(IP('1080::8:800:200C:417A'))
1080::8:800:200c:417a
>>> print(IP('::1'))
::1
>>> print(IP('::13.1.68.3'))
::d01:4403

Network mask and prefixes

>>> print(IP('127.0.0.0/8'))
127.0.0.0/8
>>> print(IP('127.0.0.0/255.0.0.0'))
127.0.0.0/8
>>> print(IP('127.0.0.0-127.255.255.255'))
127.0.0.0/8

Derive network address

IPy can transform an IP address into a network address by applying the given netmask:

>>> print(IP('127.0.0.1/255.0.0.0', make_net=True))
127.0.0.0/8

This can also be done for existing IP instances:

>>> print(IP('127.0.0.1').make_net('255.0.0.0'))
127.0.0.0/8

Convert address to string

Nearly all class methods which return a string have an optional parameter 'wantprefixlen' which controls if the prefixlen or netmask is printed. Per default the prefilen is always shown if the network contains more than one address:

wantprefixlen == 0 / None     don't return anything   1.2.3.0
wantprefixlen == 1            /prefix                 1.2.3.0/24
wantprefixlen == 2            /netmask                1.2.3.0/255.255.255.0
wantprefixlen == 3            -lastip                 1.2.3.0-1.2.3.255

You can also change the defaults on an per-object basis by fiddling with the class members:

  • NoPrefixForSingleIp
  • WantPrefixLen

Examples of string conversions:

>>> IP('10.0.0.0/32').strNormal()
'10.0.0.0'
>>> IP('10.0.0.0/24').strNormal()
'10.0.0.0/24'
>>> IP('10.0.0.0/24').strNormal(0)
'10.0.0.0'
>>> IP('10.0.0.0/24').strNormal(1)
'10.0.0.0/24'
>>> IP('10.0.0.0/24').strNormal(2)
'10.0.0.0/255.255.255.0'
>>> IP('10.0.0.0/24').strNormal(3)
'10.0.0.0-10.0.0.255'
>>> ip = IP('10.0.0.0')
>>> print(ip)
10.0.0.0
>>> ip.NoPrefixForSingleIp = None
>>> print(ip)
10.0.0.0/32
>>> ip.WantPrefixLen = 3
>>> print(ip)
10.0.0.0-10.0.0.0

Work with multiple networks

Simple addition of neighboring netblocks that can be aggregated will yield a parent network of both, but more complex range mapping and aggregation requires is available with the IPSet class which will hold any number of unique address ranges and will aggregate overlapping ranges.

>>> from IPy import IP, IPSet
>>> IP('10.0.0.0/22') - IP('10.0.2.0/24')
IPSet([IP('10.0.0.0/23'), IP('10.0.3.0/24')])
>>> IPSet([IP('10.0.0.0/23'), IP('10.0.3.0/24'), IP('10.0.2.0/24')])
IPSet([IP('10.0.0.0/22')])
>>> s = IPSet([IP('10.0.0.0/22')])
>>> s.add(IP('192.168.1.0/29'))
>>> s
IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/29')])
>>> s.discard(IP('192.168.1.2'))
>>> s
IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/31'), IP('192.168.1.3'), IP('192.168.1.4/30')])

IPSet supports the set method isdisjoint:

>>> s.isdisjoint(IPSet([IP('192.168.0.0/16')]))
False
>>> s.isdisjoint(IPSet([IP('172.16.0.0/12')]))
True

IPSet supports intersection:

>>> s & IPSet([IP('10.0.0.0/8')])
IPSet([IP('10.0.0.0/22')])

Compatibility and links

IPy 1.01 works on Python version 2.6 - 3.7.

The IP module should work in Python 2.5 as long as the subtraction operation is not used. IPSet requires features of the collections class which appear in Python 2.6, though they can be backported.

Eratta

When using IPv6 addresses, it is best to compare using IP().len() instead of len(IP). Addresses with an integer value > 64 bits can break the 2nd method. See http://stackoverflow.com/questions/15650878 for more info.

Fuzz testing for IPSet will throw spurious errors when the IPSet module combines two smaller prefixes into a larger prefix that matches the random prefix tested against.

This Python module is under BSD license: see COPYING file.

Further Information might be available at: https://github.com/autocracy/python-ipy

python-ipy's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-ipy's Issues

Feature proposal: IPSet to work with strings

Currently when you initialize IPSet it should be a list of IP objects or it will raise:

ValueError: Only IP objects can be added to an IPSet

I was wondering instead of raising error to convert it to try to convert it to IP object.

My second proposal is for improving __contains__ to be able to accept any type of objects. Currently on Python 3 when I try to use string:

'192.168.0.1' in IPSet([IP('192.168.0.0/24')])

Instead of returning True or False (preferably True) it is raising exception:

/usr/lib/python3.6/site-packages/IPy.py in __contains__(self, ip)
   1040             valid_masks = [x for x in valid_masks if x <= ip_mask]
   1041         for mask in valid_masks:
-> 1042             i = bisect.bisect(self.prefixtable[mask], ip)
   1043             # Because of sorting order, a match can only occur in the prefix
   1044             # that comes before the result of the search.

TypeError: '<' not supported between instances of 'str' and 'IP'

I think that it should be better if the following will be possible if both of my proposals are accepted:

>>> '192.168.0.1' in IPSet(['192.168.0.0/24'])
True

Do you think that my proposals are useful? If you like them I can try to prepare a PR for them.

version testing on /31(and /127) network fails

When doing .version() on a /31 or a /127 network the test throws an error with the "upper" ip.

Examples:
IP("198.51.100.3/31").version() <-- fails
IP("198.51.100.2/31").version() <-- works as expected
IP("2001:db8:2::1/127").version() <-- fails
IP("2001:db8:2::0/127").version() <-- works as expected

This can be worked around with make_net=True
IP("198.51.100.3/31", make_net=True).version() <-- now works as expected
IP("2001:db8:2::1/127", make_net=True).version() <-- now works as expected

Proposing a PR to fix a few small typos

Issue Type

[x] Bug (Typo)

Steps to Replicate and Expected Behaviour

  • Examine test/test_IPy.py and observe receipe, however expect to see recipe.
  • Examine IPy.py and observe prefered, however expect to see preferred.
  • Examine IPy.py and observe interprete, however expect to see interpret.
  • Examine IPy.py and observe genererate, however expect to see generate.
  • Examine test/test_IPy.py and observe destinguishing, however expect to see distinguishing.
  • Examine test/test_fuzz.py and observe disjointnes, however expect to see disjointness.
  • Examine README.rst and observe collecitons, however expect to see collections.

Notes

Semi-automated issue generated by
https://github.com/timgates42/meticulous/blob/master/docs/NOTE.md

To avoid wasting CI processing resources a branch with the fix has been
prepared but a pull request has not yet been created. A pull request fixing
the issue can be prepared from the link below, feel free to create it or
request @timgates42 create the PR. Alternatively if the fix is undesired please
close the issue with a small comment about the reasoning.

https://github.com/timgates42/python-ipy/pull/new/bugfix_typos

Thanks.

IPy validates invalid IPv6 address

The IPv4 part of the string is truncated, but IPy is still able to validate it.

import IPy
IPy.IP("0000:0000:0000:0000:0000:babe:220.12.22")
IP('::babe:dc0c:1600')

After adding the last octet, I get following output.

IPy.IP("0000:0000:0000:0000:0000:babe:220.12.22.2")
IP('::babe:dc0c:1602')

version used: 0.81-0

FAIL: testRandomContains (__main__.TestIPSet)

Using IPy 1.0.0. fuzzy test fails often:

+ PYTHONPATH=build-2 /usr/bin/python2 test/test_fuzz.py
...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: 0.0.0.0/0 in IPSet([IP('0.0.0.0/0')]) != False (made from [IP('20.92.0.0/14'), IP('86.4.168.0/21'), IP('28.64.0.0/10'), IP('209.103.80.0/20'), IP('234.0.0.0/8'), IP('146.197.0.0/19'), IP('128.0.0.0/1'), IP('82.176.138.136/29'), IP('178.91.164.0/23'), IP('253.153.255.212/30'), IP('161.206.83.22/31'), IP('80.96.0.0/11'), IP('128.0.0.0/4'), IP('64.0.0.0/4'), IP('151.128.0.0/10'), IP('77.173.47.0/24'), IP('37.92.0.0/14'), IP('13.118.32.0/21'), IP('162.56.0.0/13'), IP('33.82.0.0/17'), IP('64.192.168.64/28'), IP('68.160.0.0/12'), IP('0.0.0.0/1'), IP('115.46.100.32/27'), IP('142.135.131.0/25'), IP('106.0.0.0/9'), IP('155.199.0.0/16'), IP('191.228.3.64/26'), IP('197.45.128.0/17'), IP('239.32.0.0/11'), IP('186.148.236.128/30'), IP('53.177.176.244/31'), IP('74.32.112.0/20'), IP('33.0.0.0/12'), IP('21.160.181.128/25')])
+ PYTHONPATH=build-3 /usr/bin/python3 test/test_fuzz.py
........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................./IPy.py:1028: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  if not isinstance(iterable, collections.Iterable):
..............................................................................................................................................................................................................................................................................F..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 0.0.0.0/0 in IPSet([IP('0.0.0.0/0')]) != False (made from [IP('192.0.0.0/5'), IP('249.104.0.0/16'), IP('222.252.56.0/21'), IP('2.250.195.0/24'), IP('154.81.225.0/25'), IP('104.0.0.0/5'), IP('17.137.128.0/18'), IP('16.0.0.0/5'), IP('46.199.0.0/18'), IP('143.158.177.80/28'), IP('180.50.88.0/21'), IP('151.0.0.0/8'), IP('46.15.83.0/26'), IP('208.0.0.0/4'), IP('45.106.0.0/15'), IP('98.133.144.128/25'), IP('0.0.0.0/1'), IP('0.0.0.0/2'), IP('99.128.0.0/13'), IP('23.192.0.0/10'), IP('96.0.0.0/5'), IP('114.80.0.0/15'), IP('135.106.0.0/15'), IP('176.31.56.0/24'), IP('128.0.0.0/1'), IP('0.177.243.134/31'), IP('63.192.0.0/10'), IP('139.135.52.0/22'), IP('215.96.0.0/12'), IP('164.55.96.0/24'), IP('98.243.251.0/24'), IP('59.78.78.0/24'), IP('152.193.26.0/24'), IP('18.49.0.0/16'), IP('90.179.24.0/23'), IP('247.238.213.224/27'), IP('159.104.0.0/13'), IP('245.216.141.0/24'), IP('103.17.57.196/30'), IP('250.50.106.128/26'), IP('205.242.192.64/27'), IP('0.0.0.0/1'), IP('23.9.2.0/23')])

======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 128.0.0.0/1 in IPSet([IP('11.120.192.0/18'), IP('32.167.192.0/18'), IP('42.90.5.80/30'), IP('48.0.0.0/5'), IP('63.31.36.0/22'), IP('63.211.9.0/24'), IP('84.0.0.0/7'), IP('116.34.128.0/19'), IP('128.0.0.0/1')]) != False (made from [IP('63.31.36.0/22'), IP('234.36.0.0/15'), IP('240.0.0.0/7'), IP('42.90.5.80/30'), IP('32.167.192.0/18'), IP('49.213.43.0/25'), IP('128.0.0.0/2'), IP('84.0.0.0/7'), IP('192.0.0.0/2'), IP('163.249.21.224/27'), IP('63.211.9.0/24'), IP('48.0.0.0/5'), IP('239.99.126.58/31'), IP('213.0.0.0/8'), IP('116.34.128.0/19'), IP('192.0.0.0/2'), IP('11.120.192.0/18'), IP('176.0.0.0/9'), IP('53.190.89.168/29')])
+ PYTHONPATH=build-3 /usr/bin/python3 test/test_fuzz.py
........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................./IPy.py:1028: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  if not isinstance(iterable, collections.Iterable):
.........................................................................................................................................................................................................................................................................................................................................................F....................................F.....................................................F....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 128.0.0.0/2 in IPSet([IP('0.0.0.0/1'), IP('128.0.0.0/2'), IP('192.227.240.124/31'), IP('193.184.142.80/28'), IP('205.105.90.0/24'), IP('251.112.0.0/12')]) != False (made from [IP('64.0.0.0/2'), IP('192.227.240.124/31'), IP('153.162.127.128/25'), IP('0.0.0.0/2'), IP('115.80.0.0/12'), IP('26.177.208.0/20'), IP('251.112.0.0/12'), IP('111.69.182.0/23'), IP('188.226.160.0/19'), IP('139.90.194.132/31'), IP('16.82.32.0/20'), IP('8.38.84.0/25'), IP('79.0.0.0/8'), IP('205.105.90.0/24'), IP('193.184.142.80/28'), IP('190.238.11.62/31'), IP('160.0.0.0/3'), IP('127.39.153.42/31'), IP('128.0.0.0/3')])

======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 0.0.0.0/0 in IPSet([IP('0.0.0.0/0')]) != False (made from [IP('144.0.0.0/4'), IP('240.0.0.0/4'), IP('76.192.0.0/12'), IP('128.0.0.0/1'), IP('184.0.0.0/7'), IP('239.101.80.0/20'), IP('154.159.128.0/17'), IP('224.160.0.0/11'), IP('0.0.0.0/5'), IP('150.15.144.0/20'), IP('38.18.155.128/27'), IP('183.0.0.0/8'), IP('235.40.0.0/13'), IP('11.66.208.0/20'), IP('65.98.171.0/25'), IP('158.132.8.96/27'), IP('230.0.0.0/7'), IP('0.0.0.0/1'), IP('0.0.0.0/1'), IP('239.89.147.0/25'), IP('8.8.251.128/25'), IP('168.159.8.0/23'), IP('199.158.0.0/15'), IP('0.0.0.0/2'), IP('208.58.57.82/31'), IP('112.0.0.0/5'), IP('6.0.0.0/8'), IP('96.0.0.0/4'), IP('170.0.0.0/8')])

======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 0.0.0.0/0 in IPSet([IP('0.0.0.0/0')]) != False (made from [IP('173.66.146.128/29'), IP('57.57.191.160/28'), IP('221.129.194.192/26'), IP('82.0.0.0/11'), IP('44.199.61.224/27'), IP('226.0.0.0/7'), IP('0.0.0.0/1'), IP('148.2.0.0/15'), IP('172.61.158.0/29'), IP('200.106.240.0/20'), IP('128.0.0.0/1'), IP('105.68.79.12/30'), IP('88.0.0.0/5'), IP('211.110.0.0/17'), IP('15.241.130.0/27')])

======================================================================
FAIL: testRandomContains (__main__.TestIPSet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_fuzz.py", line 88, in testRandomContains
    "%s in %s != %s (made from %s)" % (question, ipset, answer, prefixes))
AssertionError: True != False : 0.0.0.0/0 in IPSet([IP('0.0.0.0/0')]) != False (made from [IP('56.0.0.0/8'), IP('198.72.192.128/25'), IP('159.18.0.0/16'), IP('255.97.0.0/16'), IP('234.150.88.0/21'), IP('176.0.0.0/5'), IP('29.153.205.40/31'), IP('129.0.0.0/11'), IP('0.0.0.0/2'), IP('18.93.0.0/16'), IP('133.119.214.0/24'), IP('70.0.0.0/9'), IP('163.151.200.0/26'), IP('139.207.65.120/29'), IP('240.0.0.0/9'), IP('193.58.160.0/20'), IP('45.77.56.104/31'), IP('236.48.229.128/25'), IP('141.0.0.0/8'), IP('32.0.0.0/3'), IP('43.66.128.0/17'), IP('37.0.0.0/9'), IP('48.143.90.248/29'), IP('216.216.0.0/13'), IP('216.0.0.0/5'), IP('41.64.0.0/10'), IP('10.128.0.0/9'), IP('162.55.122.0/25'), IP('156.0.0.0/6'), IP('128.0.0.0/1'), IP('65.81.242.64/29'), IP('0.0.0.0/3'), IP('239.156.72.0/21'), IP('125.0.0.0/8'), IP('192.0.0.0/2'), IP('63.5.64.0/21'), IP('128.0.0.0/2'), IP('183.181.72.192/29'), IP('166.6.163.128/25'), IP('0.0.0.0/1'), IP('64.0.0.0/2'), IP('167.186.52.0/22'), IP('87.32.0.0/12'), IP('142.0.0.0/8')])

----------------------------------------------------------------------

Exception raised when comparing an IP instance with None

File "/usr/lib64/python2.6/site-packages/IPy-0.71-py2.6.egg/IPy.py" in cmp

  1.     if self._prefixlen < other.prefixlen():
    

Generally anything except None to None is different. So comparing any IP instance with None should return False.

PS: The line number might have something to do with this issue as well... :-/

There is a 'small' documentation issue in IPy.PY

There is a 'minor' documentation issue in IPy.PY 
  line:569  def __len__(self):

        """Return the length of a subnet.

  Called to implement the built-in function len().
   It breaks with IPv6 Networks. Anybody knows how to fix this."""
   ...
           return int(self.len())

The reason that it breaks with ipv6 is that len() must return an integer. Calling int() before returning the result is not enough. I do not think it would be desirable to alter the current behaviour. Perhaps a note could be added to the documentation about the fact that len(IPy.IP(...) ) for ipv6 ranges may result in an exception being raised and that IPy.IP(..).len() should be used instead.

IP in IPset delivers different results for Linux and Mac OS

from IPy import IP, IPSet
reserved_ones = IPSet([
    IP('0.0.0.0/8'),
    IP('10.0.0.0/8'),
    IP('100.64.0.0/10'),
    IP('127.0.0.0/8'),
    IP('169.254.0.0/16'),
    IP('172.16.0.0/12'),
    IP('192.0.0.0/24'),
    IP('192.0.2.0/24'),
    IP('192.88.99.0/24'),
    IP('192.168.0.0/16'),
    IP('198.18.0.0/15'),
    IP('198.51.100.0/24'),
    IP('203.0.113.0/24'),
    IP('224.0.0.0/4'),
    IP('240.0.0.0/4'),
    IP('255.255.255.255/32'),
])

On
Debian 7.7
Kernel: Linux 3.2.0-3-amd64 #1 SMP Mon Jul 23 02:45:17 UTC 2012 x86_64 GNU/Linux

>>> IP('127.0.0.1') in reserved_ones
False

On Mac OS 10.9.5
Darwin 13.4.0 Darwin Kernel Version 13.4.0: Sun Aug 17 19:50:11 PDT 2014; root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64

>>> IP('127.0.0.1') in reserved_ones
True

Add pkg info in pypi repository

Hi, would it be possible to add PKG-INFO to pypi repository,
similarly as is done for https://pypi.python.org/packages/2e/ad/e627446492cc374c284e82381215dcd9a0a87c4f6e90e9789afefe6da0ad/requests-2.11.1.tar.gz#md5=ad5f9c47b5c5dfdb28363ad7546b0763

Repository managers such as Artifactory are reliant on inclusion of the PKG-INFO in the built tar.gz module files to extract metadata to enable them to correctly index the module and serve it in PyPi repositories

If this is good idea, I can contribute with a patch.

README doctest fails X 3; ipy-0.81; py3.

all py3.s, no distinction. Every which way.

IPy-0.81-python3_2 $ PYTHONPATH=lib/ python3.2 test_doc.py

=== Test file: README ===
**********************************************************************
File "README", line 145, in README
Failed example:
    IP('10.0.0.0/22') - IP('10.0.2.0/24')
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python3.2/doctest.py", line 1288, in __run
        compileflags, 1), test.globs)
      File "<doctest README[36]>", line 1, in <module>
        IP('10.0.0.0/22') - IP('10.0.2.0/24')
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 594, in __sub__
        return _remove_subprefix(self, other)
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 1600, in _remove_subprefix
        IP('%s/%d' % (prefix[prefix.len() / 2], prefix._prefixlen + 1)),
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 962, in __getitem__
        return IP(IPint.__getitem__(self, key), ipversion=self._ipversion)
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 614, in __getitem__
        raise TypeError
    TypeError
**********************************************************************
File "README", line 153, in README
Failed example:
    s.discard(IP('192.168.1.2'))
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib64/python3.2/doctest.py", line 1288, in __run
        compileflags, 1), test.globs)
      File "<doctest README[41]>", line 1, in <module>
        s.discard(IP('192.168.1.2'))
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 1118, in discard
        self.prefixes[i:i+1] = self.prefixes[i] - del_prefix
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 594, in __sub__
        return _remove_subprefix(self, other)
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 1600, in _remove_subprefix
        IP('%s/%d' % (prefix[prefix.len() / 2], prefix._prefixlen + 1)),
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 962, in __getitem__
        return IP(IPint.__getitem__(self, key), ipversion=self._ipversion)
      File "/mnt/gen2/TmpDir/portage/dev-python/ipy-0.81/work/IPy-0.81-python3_2/lib/IPy.py", line 614, in __getitem__
        raise TypeError
    TypeError
**********************************************************************
File "README", line 154, in README
Failed example:
    s
Expected:
    IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/31'), IP('192.168.1.3'), IP('192.168.1.4/30')])
Got:
    IPSet([IP('10.0.0.0/22'), IP('192.168.1.0/29')])
**********************************************************************
1 items had failures:
   3 of  43 in README
***Test Failed*** 3 failures.
=== Test file: test.rst ===
=== Test IPy module ===
=== Overall Results ===
total tests 205, failures 3

IPy forces the user to provide normalized values

Since IPy is a helper library it'd be very good if it does routines itself. Though, you can't provide a non-normalized prefix:

IP('192.156.0.12/24')  # ValueError: IP('192.156.0.12/24') has invalid prefix length (24)

I understand that it's a standard de-facto for prefixes to have their address part be the first address of their range. According to RFC1812:

Internet Address
An assigned number that identifies a host in an internet. It
has two parts: an IP address and a prefix length. The prefix
length indicates how many of the most specific bits of the
address constitute the network

So, if a library's user wants a prefix (by providing a mask along with an IP address to constructor) it would be great if IPy do the Network Address construction by itself, not enforcing a user do the same calculations as IPy does under the hood.

For example, a user has an IP as a string 192.168.99.230

The user wants to check, if another IP 192.168.99.126 belong to the same /25 network. In order to do this a user needs to construct a network address (all 7 last bits dropped to 0) for both (hence, take an integer equivalent) and then compare. By the time a user has ints and a mask, he does not need IPy at all any more.

Instead of raising, IPy could provide a user with functionality like this:

>>> ip1 = IPy.IP('192.168.99.230/25')
>>> ip1
<<< IP('192.168.99.128/25')
>>> ip2 = IPy.IP('192.168.99.126/25')
>>> ip2
<<< IP('192.168.99.0/25')
>>> ip1 == ip2
<<< False

2 failing tests on 32bit/python3 hosts

Greetings. I'm getting 2 tests failing, but only on 32bit hosts with python3.
On 64bit everything is fine. With python2 everything is ok.

+ /usr/bin/python3 test/test_IPy.py
.................................EE.......................................
======================================================================
ERROR: testNonZeroType (__main__.RegressionTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_IPy.py", line 888, in testNonZeroType
    self.assertEqual(bool(IPy.IP("0.0.0.0/0")), True)
OverflowError: cannot fit 'int' into an index-sized integer
======================================================================
ERROR: testNulNetmask (__main__.RegressionTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_IPy.py", line 881, in testNulNetmask
    if ip:
OverflowError: cannot fit 'int' into an index-sized integer
----------------------------------------------------------------------
Ran 74 tests in 0.322s
FAILED (errors=2)

Happy to gather more info, etc. This is from downstream Fedora package bug:
https://bugzilla.redhat.com/show_bug.cgi?id=1489512

Issue with IPSet iterator?

Hello,

Would like to first start off by saying that Python is not my my strong suit so apologies in advanced for any terminology I may not be using properly or improper syntax, etc.

I've been utilizing this package in conjunction with poodle-prober. I've forked the repo and I am attempting to add a CSV input/output option to the script. I'm having trouble with this PoC:

import IPy
import csv

net = IPy.IPSet()
with open('test.csv', newline='') as csvfile:
    reader = csv.reader(csvfile, delimiter=',', quotechar='"')
    for row in reader:
        net.add(IPy.IP(row[0]))

print(len(net))
count = 0
for ip in net:
    count += 1
print(count)

I'm attempting to read a CSV file which contains a list of IP addresses. The addresses are both public and private in various ranges. I believe they are being stored in the following way:

$ file test.csv
test.csv: ASCII text, with CRLF line terminators

My issue is that in my end result of my script, I've been tearing my hair out trying to figure out why the length of the output file differed from the input file. I've narrowed it down to the above code.

The total amount of IP addresses in the list for this test was 262.
I noticed that len(net) was 262 and count was 229.
If I change the IPSet object to just a regular list and do .append() instead of .add(), then the count matches 262.
Is there a reason for why this is happening? Is this a bug?

Thanks

Failed to update 0.81 -> 0.82

Hi,

I'm on CentOS 7 and want to update from IPy 0.81 to 0.82. pip tells me that the update was successfull but the old version is still present anywhere? I could successfully update other python packages so this doesn't seem to be a general pip issue.

root@puppet ~ # pip list --outdated
Could not find any downloads that satisfy the requirement yum-metadata-parser
Could not find any downloads that satisfy the requirement policycoreutils-default-encoding
Could not find any downloads that satisfy the requirement sepolicy
Could not find any downloads that satisfy the requirement slip.dbus
Could not find any downloads that satisfy the requirement javapackages
IPy (Current: 0.81 Latest: 0.82)
Could not find any downloads that satisfy the requirement seobject
root@puppet ~ # pip install --upgrade ipy
Downloading/unpacking ipy from https://pypi.python.org/packages/source/I/IPy/IPy-0.82.tar.gz#md5=0625e884df8bde533b54a88c85cf5029
  Downloading IPy-0.82.tar.gz
  Running setup.py egg_info for package ipy

Installing collected packages: ipy
  Found existing installation: IPy 0.81
    Uninstalling IPy:
      Successfully uninstalled IPy
  Running setup.py install for ipy

Successfully installed ipy
Cleaning up...
root@puppet ~ # pip list --outdated
Could not find any downloads that satisfy the requirement yum-metadata-parser
Could not find any downloads that satisfy the requirement policycoreutils-default-encoding
Could not find any downloads that satisfy the requirement sepolicy
Could not find any downloads that satisfy the requirement slip.dbus
Could not find any downloads that satisfy the requirement javapackages
IPy (Current: 0.81 Latest: 0.82)
Could not find any downloads that satisfy the requirement seobject
root@puppet ~ #

Incompatibility with Python 2.5

Recently (probably 195bbf2) support for Python 2.5 has been broken, but README file says:

IPy 0.80 works on Python version 2.5 - 3.3.

Exception with Python 2.5:

$ python2.5 -c 'import IPy'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "IPy.py", line 1016, in <module>
    class IPSet(collections.MutableSet):
AttributeError: 'module' object has no attribute 'MutableSet'

Support for Python 2.5 needs to be fixed or documentation should be changed to state that Python >=2.6 is required.

IPy fails dictionary test in test.rst on Python >=3.6

Hi

The test

>>> d={}
>>> d[IP('0.0.0.0/0')] = 1
>>> d[IP('::/0')] = 2
>>> d
{IP('::/0'): 2, IP('0.0.0.0/0'): 1}

fails because it relies on the ordering of the entries (which is an implementation detail in Python <=3.6, and part of the specification in Python 3.7 (defined it to be the insertion order[0]))

There are various ways how to fix the test, which to choose might depends on what the test should actually test.
Instead of >>> d use one of

a)

>>> len(d)
2

b)

>>> l = list(d.items()).sort()
>>> l
[(IP('0.0.0.0/0'), 1), (IP('::/0'), 2)]

c)
Remove the test, because the Python dict is guaranteed to treat different keys as different entries and IP('0.0.0.0/0') and IP('::/0') are not equal by a test above.

[0] https://docs.python.org/3/whatsnew/3.7.html#summary-release-highlights

Overflow when calling len() on IPSet

>>> len(IPSet([IP('2400::/64')]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: long int too large to convert to int
>>> len(IPSet([IP('2400::/66')]))
4611686018427387904

IPv4-mapped IPv6 address reverseName() failure

[~/py/dns]|19> IP('::ffff:193.0.1.208').reverseName()
          <19> '0.d.1.0.0.0.1.c.f.f.f.f.ip6.arpa.'

The correct output could be:

0.d.1.0.0.0.1.c.f.f.f.f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.

However, 0.ip6.arpa is never served. I would prefer IPv4-mapped addresses to be converted to IPv4 in-addr.arpa lookups:

208.1.0.193.in-addr.arpa.

wire tests in setup.py

Would be nice to see tests wired up into setup.py, so typical tests, like in other python packages, run would work. Currently:

+ /usr/bin/python2 setup.py build '--build-base=build-2' test
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

error: invalid command 'test'

__contains__ does not work correct

Hi, seems there is a failure within the __contains__ methode. Have the following code:

a = IP('80.89.214.2/31')
b = IP('80.89.214.4/30')
c = IP('80.89.214.8/31')
d = IP('80.89.214.11/32')
e = IP('80.89.214.13/32')
f = IP('80.89.214.14/31')
g = IP('80.89.214.16/30')
h = IP('80.89.214.20/31')
i = IP('80.89.214.0/31')
pool = IPSet([a, b, c, d, e, f, g, h, i])

print "%s" % pool
for x in pool:
    print "%s in %s" % (x, x in pool)
print ""
for x in (a, b, c, d, e, f, g, h, i):
    print "%s in %s" % (x, x in pool)

Which produce the following output:

IPSet([IP('80.89.214.0/29'), IP('80.89.214.8/31'), IP('80.89.214.11'), IP('80.89.214.13'), IP('80.89.214.14/31'), IP('80.89.214.16/30'), IP('80.89.214.20/31')])
80.89.214.0/29 in True
80.89.214.8/31 in False
80.89.214.11 in True
80.89.214.13 in True
80.89.214.14/31 in False
80.89.214.16/30 in True
80.89.214.20/31 in True

80.89.214.2/31 in True
80.89.214.4/30 in True
80.89.214.8/31 in False
80.89.214.11 in True
80.89.214.13 in True
80.89.214.14/31 in False
80.89.214.16/30 in True
80.89.214.20/31 in True
80.89.214.0/31 in True

But I would expect that all of them are part of the pool.

Tested with Python 2.7.6

IP Auto "fills" IPv4 with Zero

I found while using IPy to check for valid IPv4 addresses that if a user misses an octet that IPy automatically fills in the remaining content with zero. Example:

>>> from IPy import IP
>>> IP('172.16.100')
IP('172.16.100.0')
>>> IP('10.1.246')
IP('10.1.246.0')

Is this expected behavior? I would think this should raise an exception that the IPv4 address is not valid as it doesn't have the correct number of octets. The behavior is the same for Python2 and Python3

Deprecation warning in Python 3.7

IPy-1.0-py3.7.egg/IPy.py:1025: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working class IPSet(collections.MutableSet):

Request to add IPv4 and IPv6 constants

Hi,

I believe it would be nice to have global constants to compare IP address versions.

For instance,

import IPy
from IPy import IP
IP("0.0.0.0").version() == IPy.IPV4_VERSION
True
IP("0.0.0.0").version() == IPy.IPV6_VERSION
False

Another way to achieve the above would be to add functions such as is_ipv4() or is_ipv6(), so we can avoid any comparisons.

IP("0.0.0.0").is_ipv4()
True
IP("0.0.0.0").is_ipv6()
False

Do you think it is worth considering any of the above changes?

Feature Request: Allow invalid octet values

In my use case, invalid octet values are intentionally assigned to nodes that are missing IP data, so we can indicate that they are placeholder values until more information is discovered. I would like if there was a flag I could use when calling the IP function to allow this, like so:

ipAddress = IP('10.0.1.296', validate_octets=False)

IP representation with netmask not possible

Hey,

i guess it should be possible to create an object that represents an ip address + subnet mask.
this is what currently happens:

ip = IPy.IP('10.10.10.12/24')
Traceback (most recent call last):
File "", line 1, in
File "/home/schlitzer/virt_env/python3.4/lib/python3.4/site-packages/IPy.py", line 259, in init
raise ValueError("%s has invalid prefix length (%s)" % (repr(self), self._prefixlen))
ValueError: IP('10.10.10.12/24') has invalid prefix length (24)

IPv4 address expansion inconsistent with inet_aton

Currently omitted zeros in IPv4 addresses are added to the right of the provided address. This is inconsistent with inet_aton:

>>> import socket
>>> from IPy import IP
>>> IP("127.1")
IP("127.1.0.0")
>>> socket.inet_aton("127.1")
'\x7f\x00\x00\x01'

I would be happy to provide a patch if it would be considered for acceptance.

IPy.check_addr_prefixlen = False doesn't work

Hi,

It seems that IPy.check_addr_prefixlen = False does not work properly.

import IPy
IPy.check_addr_prefixlen = False
IPy.IP('80.14.164.132/255.255.255.0')
Traceback (most recent call last):
File "", line 1, in
File "IPy.py", line 248, in init
raise ValueError("%s has invalid prefix length (%s)" % (repr(self), self._prefixlen))
ValueError: IP('80.14.164.132/24') has invalid prefix length (24)
IPy.version
'0.75'

From my understanding, IPy.check_addr_prefixlen = False should prevent the error message from appearing, right?
I'm running Ubuntu 11.10 with python2.7.2+

verifying IP(input)

To whom it may concern,

when accepting input if number > 255 and no "." follows, instead of going through an error or filling empty spaces with "0". instead it accepts the input. as shown in the example below.

input_issues

Is the code supposed to behave this way, something I may be omitting for how IP address works?
Or is there something I may be doing wrong, down below is how I accept input from users.

read_input

for retrieving the input I just use a return statement of the user_ip variable.

Thanks in advance!
looking forward to learn more about this module.

Range IP exception

import IPy
print IPy.IP("127.0.0.1-127.0.0.3")
File "D:\Python27\lib\site-packages\IPy.py", line 225, in __init__
    if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last:
  File "D:\Python27\lib\site-packages\IPy.py", line 263, in __init__
    raise ValueError("%s has invalid prefix length (%s)" % (repr(self), self._prefixlen))
ValueError: IP('127.0.0.1/30') has invalid prefix length (30)

No way to force printing of host prefix lengths

Current string conversion functions don't distinguish between the default (print prefix only for non-host entities) and user-specified wantprefixlen values. The code in _printPrefix conflates None with 1, and always applies the default setting even when wantprefixlen is set. There is an instance variable to override this, but this is not very conducive to efficient scripting (this is not even exposed in the constructor for IP), and IMO breaks the expectation of 'wantprefixlen'.

In my opinion, I expect 'wantprefixlen' set to non-zero to always print the prefix length regardless of what the value is.

A pull request is incoming.

flake8 issues

These should be fixed to make the code easier to read/maintain and follow best practices

theHarvester/discovery/IPy.py:18:9: E241 multiple spaces after ':'
    '0':                'PUBLIC',   # fall back
        ^
theHarvester/discovery/IPy.py:19:16: E241 multiple spaces after ':'
    '00000000':         'PRIVATE',  # 0/8
               ^
theHarvester/discovery/IPy.py:20:16: E241 multiple spaces after ':'
    '00001010':         'PRIVATE',  # 10/8
               ^
theHarvester/discovery/IPy.py:21:18: E241 multiple spaces after ':'
    '0110010001':       'CARRIER_GRADE_NAT', #100.64/10
                 ^
theHarvester/discovery/IPy.py:21:45: E261 at least two spaces before inline comment
    '0110010001':       'CARRIER_GRADE_NAT', #100.64/10
                                            ^
theHarvester/discovery/IPy.py:21:46: E262 inline comment should start with '# '
    '0110010001':       'CARRIER_GRADE_NAT', #100.64/10
                                             ^
theHarvester/discovery/IPy.py:22:16: E241 multiple spaces after ':'
    '01111111':         'LOOPBACK', # 127.0/8
               ^
theHarvester/discovery/IPy.py:22:36: E261 at least two spaces before inline comment
    '01111111':         'LOOPBACK', # 127.0/8
                                   ^
theHarvester/discovery/IPy.py:23:9: E241 multiple spaces after ':'
    '1':                'PUBLIC',   # fall back
        ^
theHarvester/discovery/IPy.py:25:20: E241 multiple spaces after ':'
    '101011000001':     'PRIVATE',  # 172.16/12
                   ^
theHarvester/discovery/IPy.py:27:11: E241 multiple spaces after ':'
    '111':              'RESERVED', # 224/3
          ^
theHarvester/discovery/IPy.py:27:36: E261 at least two spaces before inline comment
    '111':              'RESERVED', # 224/3
                                   ^
theHarvester/discovery/IPy.py:28:5: E123 closing bracket does not match indentation of opening bracket's line
    }
    ^
theHarvester/discovery/IPy.py:35:52: E203 whitespace before ':'
    '00000000'                                      : 'RESERVED',               # ::/8
                                                   ^
theHarvester/discovery/IPy.py:36:52: E203 whitespace before ':'
    '0' * 96                                        : 'RESERVED',               # ::/96 Formerly IPV4COMP [RFC4291]
                                                   ^
theHarvester/discovery/IPy.py:37:52: E203 whitespace before ':'
    '0' * 128                                       : 'UNSPECIFIED',            # ::/128
                                                   ^
theHarvester/discovery/IPy.py:38:52: E203 whitespace before ':'
    '0' * 127 + '1'                                 : 'LOOPBACK',               # ::1/128
                                                   ^
theHarvester/discovery/IPy.py:39:52: E203 whitespace before ':'
    '0' * 80 + '1' * 16                             : 'IPV4MAP',                # ::ffff:0:0/96
                                                   ^
theHarvester/discovery/IPy.py:40:52: E203 whitespace before ':'
    '00000000011001001111111110011011' + '0' * 64   : 'WKP46TRANS',             # 0064:ff9b::/96 Well-Known-Prefix [RFC6052]
                                                   ^
theHarvester/discovery/IPy.py:41:52: E203 whitespace before ':'
    '00000001'                                      : 'UNASSIGNED',             # 0100::/8
                                                   ^
theHarvester/discovery/IPy.py:42:52: E203 whitespace before ':'
    '0000001'                                       : 'RESERVED',               # 0200::/7 Formerly NSAP [RFC4048]
                                                   ^
theHarvester/discovery/IPy.py:43:52: E203 whitespace before ':'
    '0000010'                                       : 'RESERVED',               # 0400::/7 Formerly IPX [RFC3513]
                                                   ^
theHarvester/discovery/IPy.py:44:52: E203 whitespace before ':'
    '0000011'                                       : 'RESERVED',               # 0600::/7
                                                   ^
theHarvester/discovery/IPy.py:45:52: E203 whitespace before ':'
    '00001'                                         : 'RESERVED',               # 0800::/5
                                                   ^
theHarvester/discovery/IPy.py:46:52: E203 whitespace before ':'
    '0001'                                          : 'RESERVED',               # 1000::/4
                                                   ^
theHarvester/discovery/IPy.py:47:52: E203 whitespace before ':'
    '001'                                           : 'GLOBAL-UNICAST',         # 2000::/3 [RFC4291]
                                                   ^
theHarvester/discovery/IPy.py:48:52: E203 whitespace before ':'
    '00100000000000010000000'                       : 'SPECIALPURPOSE',         # 2001::/23 [RFC4773]
                                                   ^
theHarvester/discovery/IPy.py:49:52: E203 whitespace before ':'
    '00100000000000010000000000000000'              : 'TEREDO',                 # 2001::/32 [RFC4380]
                                                   ^
theHarvester/discovery/IPy.py:50:52: E203 whitespace before ':'
    '00100000000000010000000000000010' + '0' * 16   : 'BMWG',                   # 2001:0002::/48 Benchmarking [RFC5180]
                                                   ^
theHarvester/discovery/IPy.py:51:52: E203 whitespace before ':'
    '0010000000000001000000000001'                  : 'ORCHID',                 # 2001:0010::/28 (Temp until 2014-03-21) [RFC4843]
                                                   ^
theHarvester/discovery/IPy.py:52:52: E203 whitespace before ':'
    '00100000000000010000001'                       : 'ALLOCATED APNIC',        # 2001:0200::/23
                                                   ^
theHarvester/discovery/IPy.py:53:52: E203 whitespace before ':'
    '00100000000000010000010'                       : 'ALLOCATED ARIN',         # 2001:0400::/23
                                                   ^
theHarvester/discovery/IPy.py:54:52: E203 whitespace before ':'
    '00100000000000010000011'                       : 'ALLOCATED RIPE NCC',     # 2001:0600::/23
                                                   ^
theHarvester/discovery/IPy.py:55:52: E203 whitespace before ':'
    '00100000000000010000100'                       : 'ALLOCATED RIPE NCC',     # 2001:0800::/23
                                                   ^
theHarvester/discovery/IPy.py:56:52: E203 whitespace before ':'
    '00100000000000010000101'                       : 'ALLOCATED RIPE NCC',     # 2001:0a00::/23
                                                   ^
theHarvester/discovery/IPy.py:57:52: E203 whitespace before ':'
    '00100000000000010000110'                       : 'ALLOCATED APNIC',        # 2001:0c00::/23
                                                   ^
theHarvester/discovery/IPy.py:58:52: E203 whitespace before ':'
    '00100000000000010000110110111000'              : 'DOCUMENTATION',          # 2001:0db8::/32 [RFC3849]
                                                   ^
theHarvester/discovery/IPy.py:59:52: E203 whitespace before ':'
    '00100000000000010000111'                       : 'ALLOCATED APNIC',        # 2001:0e00::/23
                                                   ^
theHarvester/discovery/IPy.py:60:52: E203 whitespace before ':'
    '00100000000000010001001'                       : 'ALLOCATED LACNIC',       # 2001:1200::/23
                                                   ^
theHarvester/discovery/IPy.py:61:52: E203 whitespace before ':'
    '00100000000000010001010'                       : 'ALLOCATED RIPE NCC',     # 2001:1400::/23
                                                   ^
theHarvester/discovery/IPy.py:62:52: E203 whitespace before ':'
    '00100000000000010001011'                       : 'ALLOCATED RIPE NCC',     # 2001:1600::/23
                                                   ^
theHarvester/discovery/IPy.py:63:52: E203 whitespace before ':'
    '00100000000000010001100'                       : 'ALLOCATED ARIN',         # 2001:1800::/23
                                                   ^
theHarvester/discovery/IPy.py:64:52: E203 whitespace before ':'
    '00100000000000010001101'                       : 'ALLOCATED RIPE NCC',     # 2001:1a00::/23
                                                   ^
theHarvester/discovery/IPy.py:65:52: E203 whitespace before ':'
    '0010000000000001000111'                        : 'ALLOCATED RIPE NCC',     # 2001:1c00::/22
                                                   ^
theHarvester/discovery/IPy.py:66:52: E203 whitespace before ':'
    '00100000000000010010'                          : 'ALLOCATED RIPE NCC',     # 2001:2000::/20
                                                   ^
theHarvester/discovery/IPy.py:67:52: E203 whitespace before ':'
    '001000000000000100110'                         : 'ALLOCATED RIPE NCC',     # 2001:3000::/21
                                                   ^
theHarvester/discovery/IPy.py:68:52: E203 whitespace before ':'
    '0010000000000001001110'                        : 'ALLOCATED RIPE NCC',     # 2001:3800::/22
                                                   ^
theHarvester/discovery/IPy.py:69:52: E203 whitespace before ':'
    '0010000000000001001111'                        : 'RESERVED',               # 2001:3c00::/22 Possible future allocation to RIPE NCC
                                                   ^
theHarvester/discovery/IPy.py:70:52: E203 whitespace before ':'
    '00100000000000010100000'                       : 'ALLOCATED RIPE NCC',     # 2001:4000::/23
                                                   ^
theHarvester/discovery/IPy.py:71:52: E203 whitespace before ':'
    '00100000000000010100001'                       : 'ALLOCATED AFRINIC',      # 2001:4200::/23
                                                   ^
theHarvester/discovery/IPy.py:72:52: E203 whitespace before ':'
    '00100000000000010100010'                       : 'ALLOCATED APNIC',        # 2001:4400::/23
                                                   ^
theHarvester/discovery/IPy.py:73:52: E203 whitespace before ':'
    '00100000000000010100011'                       : 'ALLOCATED RIPE NCC',     # 2001:4600::/23
                                                   ^
theHarvester/discovery/IPy.py:74:52: E203 whitespace before ':'
    '00100000000000010100100'                       : 'ALLOCATED ARIN',         # 2001:4800::/23
                                                   ^
theHarvester/discovery/IPy.py:75:52: E203 whitespace before ':'
    '00100000000000010100101'                       : 'ALLOCATED RIPE NCC',     # 2001:4a00::/23
                                                   ^
theHarvester/discovery/IPy.py:76:52: E203 whitespace before ':'
    '00100000000000010100110'                       : 'ALLOCATED RIPE NCC',     # 2001:4c00::/23
                                                   ^
theHarvester/discovery/IPy.py:77:52: E203 whitespace before ':'
    '00100000000000010101'                          : 'ALLOCATED RIPE NCC',     # 2001:5000::/20
                                                   ^
theHarvester/discovery/IPy.py:78:52: E203 whitespace before ':'
    '0010000000000001100'                           : 'ALLOCATED APNIC',        # 2001:8000::/19
                                                   ^
theHarvester/discovery/IPy.py:79:52: E203 whitespace before ':'
    '00100000000000011010'                          : 'ALLOCATED APNIC',        # 2001:a000::/20
                                                   ^
theHarvester/discovery/IPy.py:80:52: E203 whitespace before ':'
    '00100000000000011011'                          : 'ALLOCATED APNIC',        # 2001:b000::/20
                                                   ^
theHarvester/discovery/IPy.py:81:52: E203 whitespace before ':'
    '0010000000000010'                              : '6TO4',                   # 2002::/16 "6to4" [RFC3056]
                                                   ^
theHarvester/discovery/IPy.py:82:52: E203 whitespace before ':'
    '001000000000001100'                            : 'ALLOCATED RIPE NCC',     # 2003::/18
                                                   ^
theHarvester/discovery/IPy.py:83:52: E203 whitespace before ':'
    '001001000000'                                  : 'ALLOCATED APNIC',        # 2400::/12
                                                   ^
theHarvester/discovery/IPy.py:84:52: E203 whitespace before ':'
    '001001100000'                                  : 'ALLOCATED ARIN',         # 2600::/12
                                                   ^
theHarvester/discovery/IPy.py:85:52: E203 whitespace before ':'
    '00100110000100000000000'                       : 'ALLOCATED ARIN',         # 2610::/23
                                                   ^
theHarvester/discovery/IPy.py:86:52: E203 whitespace before ':'
    '00100110001000000000000'                       : 'ALLOCATED ARIN',         # 2620::/23
                                                   ^
theHarvester/discovery/IPy.py:87:52: E203 whitespace before ':'
    '001010000000'                                  : 'ALLOCATED LACNIC',       # 2800::/12
                                                   ^
theHarvester/discovery/IPy.py:88:52: E203 whitespace before ':'
    '001010100000'                                  : 'ALLOCATED RIPE NCC',     # 2a00::/12
                                                   ^
theHarvester/discovery/IPy.py:89:52: E203 whitespace before ':'
    '001011000000'                                  : 'ALLOCATED AFRINIC',      # 2c00::/12
                                                   ^
theHarvester/discovery/IPy.py:90:52: E203 whitespace before ':'
    '00101101'                                      : 'RESERVED',               # 2d00::/8
                                                   ^
theHarvester/discovery/IPy.py:91:52: E203 whitespace before ':'
    '0010111'                                       : 'RESERVED',               # 2e00::/7
                                                   ^
theHarvester/discovery/IPy.py:92:52: E203 whitespace before ':'
    '0011'                                          : 'RESERVED',               # 3000::/4
                                                   ^
theHarvester/discovery/IPy.py:93:52: E203 whitespace before ':'
    '010'                                           : 'RESERVED',               # 4000::/3
                                                   ^
theHarvester/discovery/IPy.py:94:52: E203 whitespace before ':'
    '011'                                           : 'RESERVED',               # 6000::/3
                                                   ^
theHarvester/discovery/IPy.py:95:52: E203 whitespace before ':'
    '100'                                           : 'RESERVED',               # 8000::/3
                                                   ^
theHarvester/discovery/IPy.py:96:52: E203 whitespace before ':'
    '101'                                           : 'RESERVED',               # a000::/3
                                                   ^
theHarvester/discovery/IPy.py:97:52: E203 whitespace before ':'
    '110'                                           : 'RESERVED',               # c000::/3
                                                   ^
theHarvester/discovery/IPy.py:98:52: E203 whitespace before ':'
    '1110'                                          : 'RESERVED',               # e000::/4
                                                   ^
theHarvester/discovery/IPy.py:99:52: E203 whitespace before ':'
    '11110'                                         : 'RESERVED',               # f000::/5
                                                   ^
theHarvester/discovery/IPy.py:100:52: E203 whitespace before ':'
    '111110'                                        : 'RESERVED',               # f800::/6
                                                   ^
theHarvester/discovery/IPy.py:101:52: E203 whitespace before ':'
    '1111110'                                       : 'ULA',                    # fc00::/7 [RFC4193]
                                                   ^
theHarvester/discovery/IPy.py:102:52: E203 whitespace before ':'
    '111111100'                                     : 'RESERVED',               # fe00::/9
                                                   ^
theHarvester/discovery/IPy.py:103:52: E203 whitespace before ':'
    '1111111010'                                    : 'LINKLOCAL',              # fe80::/10
                                                   ^
theHarvester/discovery/IPy.py:104:52: E203 whitespace before ':'
    '1111111011'                                    : 'RESERVED',               # fec0::/10 Formerly SITELOCAL [RFC4291]
                                                   ^
theHarvester/discovery/IPy.py:105:52: E203 whitespace before ':'
    '11111111'                                      : 'MULTICAST',              # ff00::/8
                                                   ^
theHarvester/discovery/IPy.py:106:52: E203 whitespace before ':'
    '1111111100000001'                              : 'NODE-LOCAL MULTICAST',   # ff01::/16
                                                   ^
theHarvester/discovery/IPy.py:107:52: E203 whitespace before ':'
    '1111111100000010'                              : 'LINK-LOCAL MULTICAST',   # ff02::/16
                                                   ^
theHarvester/discovery/IPy.py:108:52: E203 whitespace before ':'
    '1111111100000100'                              : 'ADMIN-LOCAL MULTICAST',  # ff04::/16
                                                   ^
theHarvester/discovery/IPy.py:109:52: E203 whitespace before ':'
    '1111111100000101'                              : 'SITE-LOCAL MULTICAST',   # ff05::/16
                                                   ^
theHarvester/discovery/IPy.py:110:52: E203 whitespace before ':'
    '1111111100001000'                              : 'ORG-LOCAL MULTICAST',    # ff08::/16
                                                   ^
theHarvester/discovery/IPy.py:111:52: E203 whitespace before ':'
    '1111111100001110'                              : 'GLOBAL MULTICAST',       # ff0e::/16
                                                   ^
theHarvester/discovery/IPy.py:112:52: E203 whitespace before ':'
    '1111111100001111'                              : 'RESERVED MULTICAST',     # ff0f::/16
                                                   ^
theHarvester/discovery/IPy.py:113:52: E203 whitespace before ':'
    '111111110011'                                  : 'PREFIX-BASED MULTICAST', # ff30::/12 [RFC3306]
                                                   ^
theHarvester/discovery/IPy.py:113:80: E261 at least two spaces before inline comment
    '111111110011'                                  : 'PREFIX-BASED MULTICAST', # ff30::/12 [RFC3306]
                                                                               ^
theHarvester/discovery/IPy.py:114:52: E203 whitespace before ':'
    '111111110111'                                  : 'RP-EMBEDDED MULTICAST',  # ff70::/12 [RFC3956]
                                                   ^
theHarvester/discovery/IPy.py:115:5: E123 closing bracket does not match indentation of opening bracket's line
    }
    ^
theHarvester/discovery/IPy.py:119:14: E221 multiple spaces before operator
IPV6_TEST_MAP    = 0xffffffffffffffffffffffff00000000
             ^
theHarvester/discovery/IPy.py:120:14: E221 multiple spaces before operator
IPV6_MAP_MASK    = 0x00000000000000000000ffff00000000
             ^
theHarvester/discovery/IPy.py:221:40: E226 missing whitespace around arithmetic operator
                if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last:
                                       ^
theHarvester/discovery/IPy.py:258:13: E128 continuation line under-indented for visual indent
            self._prefixlen, self._ipversion):
            ^
theHarvester/discovery/IPy.py:320:17: E711 comparison to None should be 'if cond is None:'
        if want == None:
                ^
theHarvester/discovery/IPy.py:322:21: E711 comparison to None should be 'if cond is None:'
            if want == None:
                    ^
theHarvester/discovery/IPy.py:346:35: E251 unexpected spaces around keyword / parameter equals
    def strBin(self, wantprefixlen = None):
                                  ^
theHarvester/discovery/IPy.py:346:37: E251 unexpected spaces around keyword / parameter equals
    def strBin(self, wantprefixlen = None):
                                    ^
theHarvester/discovery/IPy.py:356:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:359:15: E271 multiple spaces after keyword
        return  '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
              ^
theHarvester/discovery/IPy.py:361:42: E251 unexpected spaces around keyword / parameter equals
    def strCompressed(self, wantprefixlen = None):
                                         ^
theHarvester/discovery/IPy.py:361:44: E251 unexpected spaces around keyword / parameter equals
    def strCompressed(self, wantprefixlen = None):
                                           ^
theHarvester/discovery/IPy.py:372:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:406:38: E251 unexpected spaces around keyword / parameter equals
    def strNormal(self, wantprefixlen = None):
                                     ^
theHarvester/discovery/IPy.py:406:40: E251 unexpected spaces around keyword / parameter equals
    def strNormal(self, wantprefixlen = None):
                                       ^
theHarvester/discovery/IPy.py:415:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:427:40: E251 unexpected spaces around keyword / parameter equals
    def strFullsize(self, wantprefixlen = None):
                                       ^
theHarvester/discovery/IPy.py:427:42: E251 unexpected spaces around keyword / parameter equals
    def strFullsize(self, wantprefixlen = None):
                                         ^
theHarvester/discovery/IPy.py:436:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:441:35: E251 unexpected spaces around keyword / parameter equals
    def strHex(self, wantprefixlen = None):
                                  ^
theHarvester/discovery/IPy.py:441:37: E251 unexpected spaces around keyword / parameter equals
    def strHex(self, wantprefixlen = None):
                                    ^
theHarvester/discovery/IPy.py:450:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:456:35: E251 unexpected spaces around keyword / parameter equals
    def strDec(self, wantprefixlen = None):
                                  ^
theHarvester/discovery/IPy.py:456:37: E251 unexpected spaces around keyword / parameter equals
    def strDec(self, wantprefixlen = None):
                                    ^
theHarvester/discovery/IPy.py:465:31: E711 comparison to None should be 'if cond is None:'
        if self.WantPrefixLen == None and wantprefixlen == None:
                              ^
theHarvester/discovery/IPy.py:504:5: E303 too many blank lines (2)
    def netmask(self):
    ^
theHarvester/discovery/IPy.py:518:5: E303 too many blank lines (2)
    def strNetmask(self):
    ^
theHarvester/discovery/IPy.py:550:5: E303 too many blank lines (2)
    def __nonzero__(self):
    ^
theHarvester/discovery/IPy.py:630:5: E303 too many blank lines (3)
    def __contains__(self, item):
    ^
theHarvester/discovery/IPy.py:657:5: E303 too many blank lines (2)
    def overlaps(self, item):
    ^
theHarvester/discovery/IPy.py:683:5: E303 too many blank lines (2)
    def __str__(self):
    ^
theHarvester/discovery/IPy.py:691:5: E303 too many blank lines (2)
    def __repr__(self):
    ^
theHarvester/discovery/IPy.py:829:39: E226 missing whitespace around arithmetic operator
            ipv4 = '%s/%s' % (ipv4, 32-(128-self._prefixlen))
                                      ^
theHarvester/discovery/IPy.py:829:44: E226 missing whitespace around arithmetic operator
            ipv4 = '%s/%s' % (ipv4, 32-(128-self._prefixlen))
                                           ^
theHarvester/discovery/IPy.py:909:44: E226 missing whitespace around arithmetic operator
                nibblepart = "%s-%s" % (s[3-(self._prefixlen // 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
                                           ^
theHarvester/discovery/IPy.py:1010:52: W504 line break after binary operator
            return IP(str(IPV6_MAP_MASK + self.ip) +
                                                   ^
theHarvester/discovery/IPy.py:1011:27: E127 continuation line over-indented for visual indent
                          "/%s" % (self._prefixlen + 96))
                          ^
theHarvester/discovery/IPy.py:1014:56: W504 line break after binary operator
                return IP(str(self.ip - IPV6_MAP_MASK) +
                                                       ^
theHarvester/discovery/IPy.py:1019:1: E302 expected 2 blank lines, found 1
class IPSet(collections.MutableSet):
^
theHarvester/discovery/IPy.py:1037:13: E265 block comment should start with '# '
            #Don't dig through more-specific ranges
            ^
theHarvester/discovery/IPy.py:1068:13: E741 ambiguous variable name 'l'
            l = next(left)
            ^
theHarvester/discovery/IPy.py:1075:21: E741 ambiguous variable name 'l'
                    l = next(left)
                    ^
theHarvester/discovery/IPy.py:1082:21: E741 ambiguous variable name 'l'
                    l = next(left)
                    ^
theHarvester/discovery/IPy.py:1140:38: E226 missing whitespace around arithmetic operator
                    self.prefixes[i:i+1] = self.prefixes[i] - del_prefix
                                     ^
theHarvester/discovery/IPy.py:1149:13: E741 ambiguous variable name 'l'
            l = next(left)
            ^
theHarvester/discovery/IPy.py:1155:21: E741 ambiguous variable name 'l'
                    l = next(left)
                    ^
theHarvester/discovery/IPy.py:1171:18: E226 missing whitespace around arithmetic operator
            j = i+1
                 ^
theHarvester/discovery/IPy.py:1195:30: E226 missing whitespace around arithmetic operator
            while i < addrlen-1:
                             ^
theHarvester/discovery/IPy.py:1218:1: E302 expected 2 blank lines, found 1
def _parseAddressIPv6(ipstr):
^
theHarvester/discovery/IPy.py:1290:28: E226 missing whitespace around arithmetic operator
            if text[pos:pos+2] == "::":
                           ^
theHarvester/discovery/IPy.py:1293:29: E226 missing whitespace around arithmetic operator
                index += pos+1
                            ^
theHarvester/discovery/IPy.py:1304:66: E226 missing whitespace around arithmetic operator
        if (fill_pos is not None) and not (fill_pos <= len(items)-1):
                                                                 ^
theHarvester/discovery/IPy.py:1316:41: E226 missing whitespace around arithmetic operator
        items = items[:fill_pos] + ['0']*diff + items[fill_pos:]
                                        ^
theHarvester/discovery/IPy.py:1338:1: E302 expected 2 blank lines, found 1
def parseAddress(ipstr, ipversion=0):
^
theHarvester/discovery/IPy.py:1453:9: E741 ambiguous variable name 'l'
        l = "%032x" % ip
        ^
theHarvester/discovery/IPy.py:1464:1: E302 expected 2 blank lines, found 1
def _ipVersionToLen(version):
^
theHarvester/discovery/IPy.py:1498:13: E128 continuation line under-indented for visual indent
            '4': '0100', '5': '0101', '6': '0110', '7': '0111',
            ^
theHarvester/discovery/IPy.py:1499:13: E128 continuation line under-indented for visual indent
            '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
            ^
theHarvester/discovery/IPy.py:1500:13: E128 continuation line under-indented for visual indent
            'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
            ^
theHarvester/discovery/IPy.py:1502:1: E302 expected 2 blank lines, found 1
def _intToBin(val):
^
theHarvester/discovery/IPy.py:1516:1: E302 expected 2 blank lines, found 1
def _count1Bits(num):
^
theHarvester/discovery/IPy.py:1524:1: E302 expected 2 blank lines, found 1
def _count0Bits(num):
^
theHarvester/discovery/IPy.py:1566:15: E222 multiple spaces after operator
    if zbits <  bits - prefixlen:
              ^
theHarvester/discovery/IPy.py:1623:15: E227 missing whitespace around bitwise or shift operator
    return ((2<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
              ^
theHarvester/discovery/IPy.py:1623:26: E226 missing whitespace around arithmetic operator
    return ((2<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
                         ^
theHarvester/discovery/IPy.py:1623:29: E226 missing whitespace around arithmetic operator
    return ((2<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
                            ^
2     E123 closing bracket does not match indentation of opening bracket's line
1     E127 continuation line over-indented for visual indent
4     E128 continuation line under-indented for visual indent
80    E203 whitespace before ':'
2     E221 multiple spaces before operator
1     E222 multiple spaces after operator
13    E226 missing whitespace around arithmetic operator
1     E227 missing whitespace around bitwise or shift operator
8     E241 multiple spaces after ':'
12    E251 unexpected spaces around keyword / parameter equals
4     E261 at least two spaces before inline comment
1     E262 inline comment should start with '# '
1     E265 block comment should start with '# '
1     E271 multiple spaces after keyword
7     E302 expected 2 blank lines, found 1
7     E303 too many blank lines (2)
8     E711 comparison to None should be 'if cond is None:'
6     E741 ambiguous variable name 'l'
2     W504 line break after binary operator

reverseName on 0.0.0.0/0 returns invalid name

The name returned by IP when using top level address 0.0.0.0/0 returned a leading dot, which is an invalid domain name:

In [3]: IP('0.0.0.0/0').reverseName()
Out[3]: '.in-addr.arpa.'

The correct response should be in-addr.arpa.

on line 921 this could be changed to:

return "%s%s.in-addr.arpa." % (nibblepart, s) if nibbblepart else "in-addr.arpa."

Thanks

.broadcast() returns IPv4('0.0.0.2') for IPv6('::2/128')

return IP(IPint.broadcast(self))

missing IP version propagation as compared to
return IP(IPint.net(self), ipversion=self._ipversion)

Reproduce with:

import IPy; print(IPy.IP('::3/128').broadcast().version()) # 4

In general, I'd prefer to have .first_address() and .last_address() methods as IPv6 address has no "broadcast" part.

// random losses of edits observed //

Request: add type annotations

First of all, thanks for this great library! I found it super useful many times.

I'm writing a new project that will use IPy and I could really use support for mypy. Problem is, the library needs stub files or type annotations for that to work best. Any chance you could add those?

Please clarify license

setup.py says it is BSD license, but COPYING file doesn't contain the words "BSD License", which makes it confusing if it is or isn't BSD licensed.

Wrong decoding of IPv6 address from string and int representations

Hello,

I have come across this behavior while trying to parse an IPv6 address in the int and the string notation.

>>> ip = IPy.IP('11', ipversion=6)
>>> ip.strNormal(0)
'0:0:0:0:0:0:b00:0'
>>> ip = IPy.IP(11, ipversion=6)
>>> ip.strNormal(0)
'0:0:0:0:0:0:0:b'

I am explicitly setting the IP version to 6 and am expecting to get the same result regardless of the incoming data type. However, it seems that the IPInt class does not honor the explicit version specification and still tries to guess the version when it detects a string. The result is (since 11 is smaller than 127) that one gets an IPv4 address 11.0.0.0 encoded as an IPv6 address.

Best regards,

David Fabian

Zone identifier

IPy ip parser doesn't consider zone identifier appended to the end of the address.

This zone_id are defined in https://tools.ietf.org/html/rfc6874 and is currently returned by netfaces on python 2.7 and Ubuntu 14.04 SO. This issue causes a exception of invalid address format since apparently zone_ids are not support by IPy. I'm using IPy 0.81.

Here is an exemple:

from IPy import IP

IP('fe80::a00:27ff:fe4c:3982%eth0')

%eth0 is the zone identifier of a valid IPv6 address, but IPy returns an exception.

Push to pypi

It would be convenient to have the last few commits on pypi.

Do we still have controle over the original pypi package? I am tempted to push the latest version as is under a slightly different name but I would also prefer not adding noise.

Let us discuss what we do about ipy.

pip install outdated ipy version

Steps to reproduce

mbp-2:~ e.iskandarov$ mkvirtualenv ipy
New python executable in ipy/bin/python2.7
Also creating executable in ipy/bin/python
Installing setuptools, pip...done.
(ipy)mbp-2:~ e.iskandarov$ pip --version
pip 1.5.6 from /Users/e.iskandarov/.virtualenvs/ipy/lib/python2.7/site-packages (python 2.7)
(ipy)mbp-2:~ e.iskandarov$ pip install ipy
Downloading/unpacking ipy
  Downloading IPy-0.82.tar.gz
  Running setup.py (path:/Users/e.iskandarov/.virtualenvs/ipy/build/ipy/setup.py) egg_info for package ipy

Installing collected packages: ipy
  Running setup.py install for ipy

Successfully installed ipy
(ipy)mbp-2:~ e.iskandarov$ pip search IPy
...
IPy                       - Class and tools for handling of IPv4 and IPv6 addresses and networks
  INSTALLED: 0.81
  LATEST:    0.82a
...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.