Source code for hypothesis.strategies._internal.ipaddress

# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis/
#
# Most of this work is copyright (C) 2013-2020 David R. MacIver
# (david@drmaciver.com), but it contains contributions by others. See
# CONTRIBUTING.rst for a full list of people who may hold copyright, and
# consult the git log if you need to determine who owns an individual
# contribution.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
#
# END HEADER

from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network, ip_network
from typing import Union

from hypothesis.errors import InvalidArgument
from hypothesis.internal.validation import check_type
from hypothesis.strategies._internal.core import (
    SearchStrategy,
    binary,
    defines_strategy_with_reusable_values,
    integers,
    sampled_from,
)

# See https://www.iana.org/assignments/iana-ipv4-special-registry/
SPECIAL_IPv4_RANGES = (
    "0.0.0.0/8",
    "10.0.0.0/8",
    "100.64.0.0/10",
    "127.0.0.0/8",
    "169.254.0.0/16",
    "172.16.0.0/12",
    "192.0.0.0/24",
    "192.0.0.0/29",
    "192.0.0.8/32",
    "192.0.0.9/32",
    "192.0.0.10/32",
    "192.0.0.170/32",
    "192.0.0.171/32",
    "192.0.2.0/24",
    "192.31.196.0/24",
    "192.52.193.0/24",
    "192.88.99.0/24",
    "192.168.0.0/16",
    "192.175.48.0/24",
    "198.18.0.0/15",
    "198.51.100.0/24",
    "203.0.113.0/24",
    "240.0.0.0/4",
    "255.255.255.255/32",
)
# and https://www.iana.org/assignments/iana-ipv6-special-registry/
SPECIAL_IPv6_RANGES = (
    "::1/128",
    "::/128",
    "::ffff:0:0/96",
    "64:ff9b::/96",
    "64:ff9b:1::/48",
    "100::/64",
    "2001::/23",
    "2001::/32",
    "2001:1::1/128",
    "2001:1::2/128",
    "2001:2::/48",
    "2001:3::/32",
    "2001:4:112::/48",
    "2001:10::/28",
    "2001:20::/28",
    "2001:db8::/32",
    "2002::/16",
    "2620:4f:8000::/48",
    "fc00::/7",
    "fe80::/10",
)


[docs]@defines_strategy_with_reusable_values def ip_addresses( *, v: int = None, network: Union[str, IPv4Network, IPv6Network] = None ) -> SearchStrategy[Union[IPv4Address, IPv6Address]]: r"""Generate IP addresses - ``v=4`` for :class:`~python:ipaddress.IPv4Address`\ es, ``v=6`` for :class:`~python:ipaddress.IPv6Address`\ es, or leave unspecified to allow both versions. ``network`` may be an :class:`~python:ipaddress.IPv4Network` or :class:`~python:ipaddress.IPv6Network`, or a string representing a network such as ``"127.0.0.0/24"`` or ``"2001:db8::/32"``. As well as generating addresses within a particular routable network, this can be used to generate addresses from a reserved range listed in the `IANA <https://www.iana.org/assignments/iana-ipv4-special-registry/>`__ `registries <https://www.iana.org/assignments/iana-ipv6-special-registry/>`__. If you pass both ``v`` and ``network``, they must be for the same version. """ if v is not None: check_type(int, v, "v") if v != 4 and v != 6: raise InvalidArgument("v=%r, but only v=4 or v=6 are valid" % (v,)) if network is None: # We use the reserved-address registries to boost the chance # of generating one of the various special types of address. four = binary(min_size=4, max_size=4).map(IPv4Address) | sampled_from( SPECIAL_IPv4_RANGES ).flatmap(lambda network: ip_addresses(network=network)) six = binary(min_size=16, max_size=16).map(IPv6Address) | sampled_from( SPECIAL_IPv6_RANGES ).flatmap(lambda network: ip_addresses(network=network)) if v == 4: return four if v == 6: return six return four | six if isinstance(network, str): network = ip_network(network) check_type((IPv4Network, IPv6Network), network, "network") assert isinstance(network, (IPv4Network, IPv6Network)) # for Mypy if v not in (None, network.version): raise InvalidArgument("v=%r is incompatible with network=%r" % (v, network)) addr_type = IPv4Address if network.version == 4 else IPv6Address return integers(int(network[0]), int(network[-1])).map(addr_type) # type: ignore