"""Provides data related to internet."""
import typing as t
import urllib.error
import urllib.parse
import urllib.request
from base64 import b64encode
from ipaddress import IPv4Address, IPv6Address
from mimesis.datasets import (
CONTENT_ENCODING_DIRECTIVES,
CORS_OPENER_POLICIES,
CORS_RESOURCE_POLICIES,
HTTP_METHODS,
HTTP_SERVERS,
HTTP_STATUS_CODES,
HTTP_STATUS_MSGS,
PUBLIC_DNS,
TLD,
USER_AGENTS,
USERNAMES,
)
from mimesis.enums import (
DSNType,
IPv4Purpose,
Locale,
MimeType,
PortRange,
TLDType,
URLScheme,
)
from mimesis.providers.base import BaseProvider
from mimesis.providers.code import Code
from mimesis.providers.date import Datetime
from mimesis.providers.file import File
from mimesis.providers.text import Text
from mimesis.types import Keywords
__all__ = ["Internet"]
[docs]
class Internet(BaseProvider):
"""Class for generating data related to the internet."""
_MAX_IPV4: t.Final[int] = (2**32) - 1
_MAX_IPV6: t.Final[int] = (2**128) - 1
[docs]
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
"""Initialize attributes.
:param args: Arguments.
:param kwargs: Keyword arguments.
"""
super().__init__(*args, **kwargs)
self._file = File(
seed=self.seed,
random=self.random,
)
self._code = Code(
seed=self.seed,
random=self.random,
)
self._text = Text(
locale=Locale.EN,
seed=self.seed,
random=self.random,
)
self._datetime = Datetime(
locale=Locale.EN,
seed=self.seed,
random=self.random,
)
class Meta:
name = "internet"
[docs]
def content_type(self, mime_type: MimeType | None = None) -> str:
"""Generates a random HTTP content type.
:return: Content type.
:Example:
application/json
"""
return self._file.mime_type(type_=mime_type)
[docs]
def dsn(self, dsn_type: DSNType | None = None, **kwargs: t.Any) -> str:
"""Generates a random DSN (Data Source Name).
:param dsn_type: DSN type.
:param kwargs: Additional keyword-arguments for hostname method.
"""
hostname = self.hostname(**kwargs)
scheme, port = self.validate_enum(dsn_type, DSNType)
return f"{scheme}://{hostname}:{port}"
[docs]
def http_status_message(self) -> str:
"""Generates a random HTTP status message.
:return: HTTP status message.
:Example:
200 OK
"""
return self.random.choice(HTTP_STATUS_MSGS)
[docs]
def http_status_code(self) -> int:
"""Generates a random HTTP status code.
:return: HTTP status.
:Example:
200
"""
return self.random.choice(HTTP_STATUS_CODES)
[docs]
def http_method(self) -> str:
"""Generates a random HTTP method.
:return: HTTP method.
:Example:
POST
"""
return self.random.choice(HTTP_METHODS)
[docs]
def ip_v4_object(self) -> IPv4Address:
"""Generates a random :py:class:`ipaddress.IPv4Address` object.
If you only need special purpose IPv4 addresses,
use :meth:`special_ip_v4_object`.
:return: :py:class:`ipaddress.IPv4Address` object.
"""
return IPv4Address(
self.random.randint(0, self._MAX_IPV4),
)
[docs]
def ip_v4_with_port(self, port_range: PortRange = PortRange.ALL) -> str:
"""Generates a random IPv4 address as string.
:param port_range: PortRange enum object.
:return: IPv4 address as string.
:Example:
19.121.223.58:8000
"""
addr = self.ip_v4()
port = self.port(port_range)
return f"{addr}:{port}"
[docs]
def ip_v4(self) -> str:
"""Generates a random IPv4 address as string.
:Example:
19.121.223.58
"""
return str(self.ip_v4_object())
[docs]
def ip_v6_object(self) -> IPv6Address:
"""Generates random :py:class:`ipaddress.IPv6Address` object.
:return: :py:class:`ipaddress.IPv6Address` object.
"""
return IPv6Address(
self.random.randint(
0,
self._MAX_IPV6,
),
)
[docs]
def ip_v6(self) -> str:
"""Generates a random IPv6 address as string.
:return: IPv6 address string.
:Example:
2001:c244:cf9d:1fb1:c56d:f52c:8a04:94f3
"""
return str(self.ip_v6_object())
[docs]
def asn(self) -> str:
"""Generates a random 4-byte ASN.
ASNs reserved for private use are not considered.
:return: ASN number as a string.
:Example:
AS123456
"""
ranges = (1, 4_199_999_999)
number = self.random.randint(*ranges)
return f"AS{number}"
[docs]
def mac_address(self) -> str:
"""Generates a random MAC address.
:return: Random MAC address.
:Example:
00:16:3e:25:e7:f1
"""
mac_hex = [
0x00,
0x16,
0x3E,
self.random.randint(0x00, 0x7F),
self.random.randint(0x00, 0xFF),
self.random.randint(0x00, 0xFF),
]
mac = [f"{x:02x}" for x in mac_hex]
return ":".join(mac)
[docs]
@staticmethod
def stock_image_url(
width: int | str = 1920,
height: int | str = 1080,
keywords: Keywords | None = None,
) -> str:
"""Generates a random stock image URL hosted on Unsplash.
See «Random search term» on https://source.unsplash.com/
for more details.
:param width: Width of the image.
:param height: Height of the image.
:param keywords: Sequence of search keywords.
:return: URL of the image.
"""
if keywords is not None:
keywords_str = ",".join(keywords)
else:
keywords_str = ""
return f"https://source.unsplash.com/{width}x{height}?{keywords_str}"
[docs]
def hostname(
self,
tld_type: TLDType | None = None,
subdomains: list[str] | None = None,
) -> str:
"""Generates a random hostname without a scheme.
:param tld_type: TLDType.
:param subdomains: List of subdomains (make sure they are valid).
:return: Hostname.
"""
tld = self.tld(tld_type=tld_type)
host = self.random.choice(USERNAMES)
if subdomains:
subdomain = self.random.choice(subdomains)
host = f"{subdomain}.{host}"
return f"{host}{tld}"
[docs]
def url(
self,
scheme: URLScheme | None = URLScheme.HTTPS,
port_range: PortRange | None = None,
tld_type: TLDType | None = None,
subdomains: list[str] | None = None,
) -> str:
"""Generates a random URL.
:param scheme: The scheme.
:param port_range: PortRange enum object.
:param tld_type: TLDType.
:param subdomains: List of subdomains (make sure they are valid).
:return: URL.
"""
host = self.hostname(tld_type, subdomains)
url_scheme = self.validate_enum(scheme, URLScheme)
url = f"{url_scheme}://{host}"
if port_range is not None:
url = f"{url}:{self.port(port_range)}"
return f"{url}/"
[docs]
def uri(
self,
scheme: URLScheme | None = URLScheme.HTTPS,
tld_type: TLDType | None = None,
subdomains: list[str] | None = None,
query_params_count: int | None = None,
) -> str:
"""Generates a random URI.
:param scheme: Scheme.
:param tld_type: TLDType.
:param subdomains: List of subdomains (make sure they are valid).
:param query_params_count: Query params.
:return: URI.
"""
directory = (
self._datetime.date(start=2010, end=self._datetime._CURRENT_YEAR)
.strftime("%Y-%m-%d")
.replace("-", "/")
)
url = self.url(scheme, None, tld_type, subdomains)
uri = f"{url}{directory}/{self.slug()}"
if query_params_count:
uri += f"?{self.query_string(query_params_count)}"
return uri
[docs]
def query_string(self, length: int | None = None) -> str:
"""Generates an arbitrary query string of given length.
:param length: Length of query string.
:return: Query string.
"""
return urllib.parse.urlencode(self.query_parameters(length))
[docs]
def query_parameters(self, length: int | None = None) -> dict[str, str]:
"""Generates an arbitrary query parameters as a dict.
:param length: Length of query parameters dictionary (maximum is 32).
:return: Dict of query parameters.
"""
def pick_unique_words(quantity: int = 5) -> list[str]:
words: set[str] = set()
while len(words) != quantity:
words.add(self._text.word())
return list(words)
if not length:
length = self.random.randint(1, 10)
if length > 32:
raise ValueError("Maximum allowed length of query parameters is 32.")
return dict(zip(pick_unique_words(length), self._text.words(length)))
[docs]
def top_level_domain(self, tld_type: TLDType = TLDType.CCTLD) -> str:
"""Generates random top level domain.
:param tld_type: Enum object :class:`enums.TLDType`
:return: Top level domain.
:raises NonEnumerableError: if tld_type not in :class:`enums.TLDType`.
"""
key = self.validate_enum(item=tld_type, enum=TLDType)
return self.random.choice(TLD[key])
[docs]
def tld(self, *args: t.Any, **kwargs: t.Any) -> str:
"""Generates a random TLD.
An alias for :meth:`top_level_domain`
"""
return self.top_level_domain(*args, **kwargs)
[docs]
def user_agent(self) -> str:
"""Get a random user agent.
:return: User agent.
:Example:
Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0)
Gecko/20100101 Firefox/15.0.1
"""
return self.random.choice(USER_AGENTS)
[docs]
def port(self, port_range: PortRange = PortRange.ALL) -> int:
"""Generates a random port.
:param port_range: PortRange enum object.
:return: Port number.
:raises NonEnumerableError: if port_range is not in PortRange.
:Example:
8080
"""
rng = self.validate_enum(port_range, PortRange)
return self.random.randint(*rng)
[docs]
def path(self, *args: t.Any, **kwargs: t.Any) -> str:
"""Generates a random path.
:param args: Arguments to pass to :meth:`slug`.
:param kwargs: Keyword arguments to pass to :meth:`slug`.
:return: Path.
"""
return self.slug(*args, **kwargs).replace("-", "/")
[docs]
def slug(self, parts_count: int | None = None) -> str:
"""Generates a random slug of given parts count.
:param parts_count: Slug's parts count.
:return: Slug.
"""
if not parts_count:
parts_count = self.random.randint(2, 12)
if parts_count > 12:
raise ValueError("Slug's parts count must be <= 12")
if parts_count < 2:
raise ValueError("Slug must contain more than 2 parts")
return "-".join(self._text.words(parts_count))
[docs]
def public_dns(self) -> str:
"""Generates a random public DNS.
:Example:
1.1.1.1
"""
return self.random.choice(PUBLIC_DNS)
[docs]
def special_ip_v4_object(self, purpose: IPv4Purpose | None = None) -> IPv4Address:
"""Generates a special purpose IPv4 address.
:param purpose: Enum object :class:`enums.IPv4Purpose`.
:return: IPv4 address.
:raises NonEnumerableError: if purpose not in :class:`enums.IPv4Purpose`.
"""
ranges = self.validate_enum(purpose, IPv4Purpose)
number = self.random.randint(*ranges)
return IPv4Address(number)
[docs]
def special_ip_v4(self, purpose: IPv4Purpose | None = None) -> str:
"""Generates a special purpose IPv4 address as string.
:param purpose: Enum object :class:`enums.IPv4Purpose`.
:return: IPv4 address as string.
"""
return str(self.special_ip_v4_object(purpose))