Repository: joke2k/faker
Pull request #327 (2016-02-18 15:41:27)

IPv4 and IPv6 network generators.
Adds optional IPv4 and IPv6 network address generation with valid CIDR notation.

Also add corresponding unit-tests.

Usage example:
```python
>>> from faker import Factory as FakerFactory
>>> faker = FakerFactory.create()
>>> faker.ipv4()
u'94.84.244.87'
>>> faker.ipv4(network=True)
u'105.0.0.0/10'
>>> faker.ipv4(network=True)
u'131.46.122.140/30'
>>> faker.ipv6()
u'0b9e:425c:8028:ffd1:88a1:9d54:a4a0:cafb'
>>> faker.ipv6(network=True)
u'1e1b:0791:da18:0000:0000:0000:0000:0000/45'
>>> faker.ipv6(network=True)
u'79c1:84fc:53b8:c9a8:23ca:7407:a775:0180/121'
```


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 90b14687..19364f54 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,6 +24,8 @@ Changelog
 * Add Maestro credit card. Thanks @anthonylauzon.
 * Add ``hr_HR`` localization. Thanks @mislavcimpersak.
 * Update ``de_DE`` first names. Thanks @WarrenFaith and @mschoebel.
+* Allow generation of IPv4 and IPv6 network address with valid CIDR. Thanks @kdeldycke.
+* Unittest IPv4 and IPv6 address and network generation. Thanks @kdeldycke.
 
 `0.5.3 - 21-September-2015 <http://github.com/joke2k/faker/compare/v0.5.2...v0.5.3>`__
 --------------------------------------------------------------------------------------
diff --git a/faker/providers/internet/__init__.py b/faker/providers/internet/__init__.py
index c4b87fdf..32bc0cf2 100644
--- a/faker/providers/internet/__init__.py
+++ b/faker/providers/internet/__init__.py
@@ -3,10 +3,13 @@
 from __future__ import unicode_literals
 from .. import BaseProvider
 
+from ipaddress import ip_address, ip_network, IPV4LENGTH, IPV6LENGTH
+
 from faker.generator import random
 from faker.providers.lorem.la import Provider as Lorem
 from faker.utils.decorators import slugify, slugify_unicode
 
+
 localized = True
 
 
@@ -99,15 +102,23 @@ class Provider(BaseProvider):
         pattern = self.random_element(self.url_formats)
         return self.generator.parse(pattern)
 
-    def ipv4(self):
-        """
-        Convert 32-bit integer to dotted IPv4 address.
-        """
-        return ".".join(map(lambda n: str(random.randint(-2147483648, 2147483647) >> n & 0xFF), [24, 16, 8, 0]))
-
-    def ipv6(self):
-        res = [hex(random.randint(0, 65535))[2:].zfill(4) for i in range(0, 8)]
-        return ":".join(res)
+    def ipv4(self, network=False):
+        """ Produce a random IPv4 address or network with a valid CIDR. """
+        address = str(ip_address(random.randint(
+            0, (2 ** IPV4LENGTH) - 1)))
+        if network:
+            address += '/' + str(random.randint(0, IPV4LENGTH))
+            address = str(ip_network(address, strict=False))
+        return address
+
+    def ipv6(self, network=False):
+        """ Produce a random IPv6 address or network with a valid CIDR. """
+        address = str(ip_address(random.randint(
+            2 ** IPV4LENGTH, (2 ** IPV6LENGTH) - 1)))
+        if network:
+            address += '/' + str(random.randint(0, IPV6LENGTH))
+            address = str(ip_network(address, strict=False))
+        return address
 
     def mac_address(self):
         mac = [random.randint(0x00, 0xff) for i in range(0, 6)]
diff --git a/setup.py b/setup.py
index e2767e3a..e4045a03 100644
--- a/setup.py
+++ b/setup.py
@@ -37,6 +37,8 @@ setup(
         'Intended Audience :: Developers',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.4',
         'Topic :: Software Development :: Libraries :: Python Modules',
@@ -58,7 +60,18 @@ setup(
         "six",
     ],
     extras_require={
-        ':python_version=="2.6"': ['importlib'],
-        ':python_version=="3.0"': ['importlib'],
+        ':python_version=="2.6"': [
+            'importlib',
+            'ipaddress',
+        ],
+        ':python_version=="2.7"': [
+            'ipaddress',
+        ],
+        ':python_version=="3.0"': [
+            'importlib',
+        ],
+        ':python_version=="3.2"': [
+            'ipaddress',
+        ],
     }
 )


Test patch:
diff --git a/faker/tests/__init__.py b/faker/tests/__init__.py
index 802bee4c..40e25e0f 100644
--- a/faker/tests/__init__.py
+++ b/faker/tests/__init__.py
@@ -7,6 +7,7 @@ __loader__ = None
 import datetime
 import json
 import os
+import re
 import time
 import unittest
 import string
@@ -535,6 +536,45 @@ class FactoryTestCase(unittest.TestCase):
             email = factory.email()
             self.assertTrue('@' in email)
 
+    def test_ipv4(self):
+        from faker.providers.internet import Provider
+
+        provider = Provider(None)
+
+        for _ in range(999):
+            address = provider.ipv4()
+            self.assertTrue(len(address) >= 7)
+            self.assertTrue(len(address) <= 15)
+            self.assertTrue(
+                re.compile(r'^(\d{1,3}\.){3}\d{1,3}$').search(address))
+
+        for _ in range(999):
+            address = provider.ipv4(network=True)
+            self.assertTrue(len(address) >= 9)
+            self.assertTrue(len(address) <= 18)
+            self.assertTrue(
+                re.compile(r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$').search(address))
+
+    def test_ipv6(self):
+        from faker.providers.internet import Provider
+
+        provider = Provider(None)
+
+        for _ in range(999):
+            address = provider.ipv6()
+            self.assertTrue(len(address) >= 3)  # ::1
+            self.assertTrue(len(address) <= 39)
+            self.assertTrue(
+                re.compile(r'^([0-9a-f]{0,4}:){2,7}[0-9a-f]{1,4}$').search(address))
+
+        for _ in range(999):
+            address = provider.ipv6(network=True)
+            self.assertTrue(len(address) >= 4)  # ::/8
+            self.assertTrue(len(address) <= 39 + 4)
+            self.assertTrue(
+                re.compile(r'^([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/\d{1,3}$').search(
+                    address))
+
 
 class GeneratorTestCase(unittest.TestCase):