Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/faker/providers/internet/__init__.py: 27%
244 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from ipaddress import IPV4LENGTH, IPV6LENGTH, IPv4Network, IPv6Address, IPv6Network
2from typing import Dict, List, Optional, Tuple
4from ...decode import unidecode
5from ...utils.decorators import lowercase, slugify, slugify_unicode
6from ...utils.distribution import choices_distribution
7from .. import BaseProvider, ElementsType
9localized = True
12class _IPv4Constants:
13 """
14 IPv4 network constants used to group networks into different categories.
15 Structure derived from `ipaddress._IPv4Constants`.
17 Excluded network list is updated to comply with current IANA list of
18 private and reserved networks.
19 """
21 _network_classes: Dict[str, IPv4Network] = {
22 "a": IPv4Network("0.0.0.0/1"),
23 "b": IPv4Network("128.0.0.0/2"),
24 "c": IPv4Network("192.0.0.0/3"),
25 }
27 # Three common private networks from class A, B and CIDR
28 # to generate private addresses from.
29 _private_networks: List[IPv4Network] = [
30 IPv4Network("10.0.0.0/8"),
31 IPv4Network("172.16.0.0/12"),
32 IPv4Network("192.168.0.0/16"),
33 ]
35 # List of networks from which IP addresses will never be generated,
36 # includes other private IANA and reserved networks from
37 # https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
38 _excluded_networks: List[IPv4Network] = [
39 IPv4Network("0.0.0.0/8"),
40 IPv4Network("100.64.0.0/10"),
41 IPv4Network("127.0.0.0/8"), # loopback network
42 IPv4Network("169.254.0.0/16"), # linklocal network
43 IPv4Network("192.0.0.0/24"),
44 IPv4Network("192.0.2.0/24"),
45 IPv4Network("192.31.196.0/24"),
46 IPv4Network("192.52.193.0/24"),
47 IPv4Network("192.88.99.0/24"),
48 IPv4Network("192.175.48.0/24"),
49 IPv4Network("198.18.0.0/15"),
50 IPv4Network("198.51.100.0/24"),
51 IPv4Network("203.0.113.0/24"),
52 IPv4Network("224.0.0.0/4"), # multicast network
53 IPv4Network("240.0.0.0/4"),
54 IPv4Network("255.255.255.255/32"),
55 ]
58class Provider(BaseProvider):
59 safe_domain_names: ElementsType[str] = ("example.org", "example.com", "example.net")
60 free_email_domains: ElementsType[str] = ("gmail.com", "yahoo.com", "hotmail.com")
61 tlds: ElementsType[str] = (
62 "com",
63 "com",
64 "com",
65 "com",
66 "com",
67 "com",
68 "biz",
69 "info",
70 "net",
71 "org",
72 )
73 hostname_prefixes: ElementsType[str] = (
74 "db",
75 "srv",
76 "desktop",
77 "laptop",
78 "lt",
79 "email",
80 "web",
81 )
82 uri_pages: ElementsType[str] = (
83 "index",
84 "home",
85 "search",
86 "main",
87 "post",
88 "homepage",
89 "category",
90 "register",
91 "login",
92 "faq",
93 "about",
94 "terms",
95 "privacy",
96 "author",
97 )
98 uri_paths: ElementsType[str] = (
99 "app",
100 "main",
101 "wp-content",
102 "search",
103 "category",
104 "tag",
105 "categories",
106 "tags",
107 "blog",
108 "posts",
109 "list",
110 "explore",
111 )
112 uri_extensions: ElementsType[str] = (
113 ".html",
114 ".html",
115 ".html",
116 ".htm",
117 ".htm",
118 ".php",
119 ".php",
120 ".jsp",
121 ".asp",
122 )
123 http_methods: ElementsType[str] = (
124 "GET",
125 "HEAD",
126 "POST",
127 "PUT",
128 "DELETE",
129 "CONNECT",
130 "OPTIONS",
131 "TRACE",
132 "PATCH",
133 )
135 user_name_formats: ElementsType[str] = (
136 "{{last_name}}.{{first_name}}",
137 "{{first_name}}.{{last_name}}",
138 "{{first_name}}##",
139 "?{{last_name}}",
140 )
141 email_formats: ElementsType[str] = (
142 "{{user_name}}@{{domain_name}}",
143 "{{user_name}}@{{free_email_domain}}",
144 )
145 url_formats: ElementsType[str] = (
146 "www.{{domain_name}}/",
147 "{{domain_name}}/",
148 )
149 uri_formats: ElementsType[str] = (
150 "{{url}}",
151 "{{url}}{{uri_page}}/",
152 "{{url}}{{uri_page}}{{uri_extension}}",
153 "{{url}}{{uri_path}}/{{uri_page}}/",
154 "{{url}}{{uri_path}}/{{uri_page}}{{uri_extension}}",
155 )
156 image_placeholder_services: ElementsType[str] = (
157 "https://picsum.photos/{width}/{height}",
158 "https://dummyimage.com/{width}x{height}",
159 "https://placekitten.com/{width}/{height}",
160 "https://placeimg.com/{width}/{height}/any",
161 )
163 replacements: Tuple[Tuple[str, str], ...] = ()
165 def _to_ascii(self, string: str) -> str:
166 for search, replace in self.replacements:
167 string = string.replace(search, replace)
169 string = unidecode(string)
170 return string
172 @lowercase
173 def email(self, safe: bool = True, domain: Optional[str] = None) -> str:
174 if domain:
175 email = f"{self.user_name()}@{domain}"
176 elif safe:
177 email = f"{self.user_name()}@{self.safe_domain_name()}"
178 else:
179 pattern: str = self.random_element(self.email_formats)
180 email = "".join(self.generator.parse(pattern).split(" "))
181 return email
183 @lowercase
184 def safe_domain_name(self) -> str:
185 return self.random_element(self.safe_domain_names)
187 @lowercase
188 def safe_email(self) -> str:
189 return self.user_name() + "@" + self.safe_domain_name()
191 @lowercase
192 def free_email(self) -> str:
193 return self.user_name() + "@" + self.free_email_domain()
195 @lowercase
196 def company_email(self) -> str:
197 return self.user_name() + "@" + self.domain_name()
199 @lowercase
200 def free_email_domain(self) -> str:
201 return self.random_element(self.free_email_domains)
203 @lowercase
204 def ascii_email(self) -> str:
205 pattern: str = self.random_element(self.email_formats)
206 return self._to_ascii(
207 "".join(self.generator.parse(pattern).split(" ")),
208 )
210 @lowercase
211 def ascii_safe_email(self) -> str:
212 return self._to_ascii(self.user_name() + "@" + self.safe_domain_name())
214 @lowercase
215 def ascii_free_email(self) -> str:
216 return self._to_ascii(
217 self.user_name() + "@" + self.free_email_domain(),
218 )
220 @lowercase
221 def ascii_company_email(self) -> str:
222 return self._to_ascii(
223 self.user_name() + "@" + self.domain_name(),
224 )
226 @slugify_unicode
227 def user_name(self) -> str:
228 pattern: str = self.random_element(self.user_name_formats)
229 return self._to_ascii(self.bothify(self.generator.parse(pattern)).lower())
231 @lowercase
232 def hostname(self, levels: int = 1) -> str:
233 """
234 Produce a hostname with specified number of subdomain levels.
236 >>> hostname()
237 db-01.nichols-phillips.com
238 >>> hostname(0)
239 laptop-56
240 >>> hostname(2)
241 web-12.williamson-hopkins.jackson.com
242 """
243 hostname_prefix: str = self.random_element(self.hostname_prefixes)
244 hostname_prefix_first_level: str = hostname_prefix + "-" + self.numerify("##")
245 return (
246 hostname_prefix_first_level if levels < 1 else hostname_prefix_first_level + "." + self.domain_name(levels)
247 )
249 @lowercase
250 def domain_name(self, levels: int = 1) -> str:
251 """
252 Produce an Internet domain name with the specified number of
253 subdomain levels.
255 >>> domain_name()
256 nichols-phillips.com
257 >>> domain_name(2)
258 williamson-hopkins.jackson.com
259 """
260 if levels < 1:
261 raise ValueError("levels must be greater than or equal to 1")
262 if levels == 1:
263 return self.domain_word() + "." + self.tld()
264 return self.domain_word() + "." + self.domain_name(levels - 1)
266 @lowercase
267 @slugify_unicode
268 def domain_word(self) -> str:
269 company: str = self.generator.format("company")
270 company_elements: List[str] = company.split(" ")
271 return self._to_ascii(company_elements.pop(0))
273 def dga(
274 self,
275 year: Optional[int] = None,
276 month: Optional[int] = None,
277 day: Optional[int] = None,
278 tld: Optional[str] = None,
279 length: Optional[int] = None,
280 ) -> str:
281 """Generates a domain name by given date
282 https://en.wikipedia.org/wiki/Domain_generation_algorithm
284 :type year: int
285 :type month: int
286 :type day: int
287 :type tld: str
288 :type length: int
289 :rtype: str
290 """
292 domain = ""
293 year = year or self.random_int(min=1, max=9999)
294 month = month or self.random_int(min=1, max=12)
295 day = day or self.random_int(min=1, max=30)
296 tld = tld or self.tld()
297 length = length or self.random_int(min=2, max=63)
299 for _ in range(length):
300 year = ((year ^ 8 * year) >> 11) ^ ((year & 0xFFFFFFF0) << 17)
301 month = ((month ^ 4 * month) >> 25) ^ 16 * (month & 0xFFFFFFF8)
302 day = ((day ^ (day << 13)) >> 19) ^ ((day & 0xFFFFFFFE) << 12)
303 domain += chr(((year ^ month ^ day) % 25) + 97)
305 return domain + "." + tld
307 def tld(self) -> str:
308 return self.random_element(self.tlds)
310 def http_method(self) -> str:
311 """Returns random HTTP method
312 https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
314 :rtype: str
315 """
317 return self.random_element(self.http_methods)
319 def url(self, schemes: Optional[List[str]] = None) -> str:
320 """
321 :param schemes: a list of strings to use as schemes, one will chosen randomly.
322 If None, it will generate http and https urls.
323 Passing an empty list will result in schemeless url generation like "://domain.com".
324 :return: a random url string.
326 """
327 if schemes is None:
328 schemes = ["http", "https"]
330 pattern: str = f'{self.random_element(schemes) if schemes else ""}://{self.random_element(self.url_formats)}'
332 return self.generator.parse(pattern)
334 def _get_all_networks_and_weights(self, address_class: Optional[str] = None) -> Tuple[List[IPv4Network], List[int]]:
335 """
336 Produces a 2-tuple of valid IPv4 networks and corresponding relative weights
338 :param address_class: IPv4 address class (a, b, or c)
339 """
340 # If `address_class` has an unexpected value, use the whole IPv4 pool
341 if address_class in _IPv4Constants._network_classes.keys():
342 networks_attr = f"_cached_all_class_{address_class}_networks"
343 all_networks = [_IPv4Constants._network_classes[address_class]] # type: ignore
344 else:
345 networks_attr = "_cached_all_networks"
346 all_networks = [IPv4Network("0.0.0.0/0")]
348 # Return cached network and weight data if available
349 weights_attr = f"{networks_attr}_weights"
350 if hasattr(self, networks_attr) and hasattr(self, weights_attr):
351 return getattr(self, networks_attr), getattr(self, weights_attr)
353 # Otherwise, compute for list of networks (excluding special networks)
354 all_networks = self._exclude_ipv4_networks(
355 all_networks,
356 _IPv4Constants._excluded_networks,
357 )
359 # Then compute for list of corresponding relative weights
360 weights = [network.num_addresses for network in all_networks]
362 # Then cache and return results
363 setattr(self, networks_attr, all_networks)
364 setattr(self, weights_attr, weights)
365 return all_networks, weights
367 def _get_private_networks_and_weights(
368 self,
369 address_class: Optional[str] = None,
370 ) -> Tuple[List[IPv4Network], List[int]]:
371 """
372 Produces an OrderedDict of valid private IPv4 networks and corresponding relative weights
374 :param address_class: IPv4 address class (a, b, or c)
375 """
376 # If `address_class` has an unexpected value, choose a valid value at random
377 if not address_class or address_class not in _IPv4Constants._network_classes.keys():
378 address_class = self.ipv4_network_class()
380 # Return cached network and weight data if available for a specific address class
381 networks_attr = f"_cached_private_class_{address_class}_networks"
382 weights_attr = f"{networks_attr}_weights"
383 if hasattr(self, networks_attr) and hasattr(self, weights_attr):
384 return getattr(self, networks_attr), getattr(self, weights_attr)
386 # Otherwise, compute for list of private networks (excluding special networks)
387 supernet = _IPv4Constants._network_classes[address_class]
388 private_networks = [subnet for subnet in _IPv4Constants._private_networks if subnet.overlaps(supernet)]
389 private_networks = self._exclude_ipv4_networks(
390 private_networks,
391 _IPv4Constants._excluded_networks,
392 )
394 # Then compute for list of corresponding relative weights
395 weights = [network.num_addresses for network in private_networks]
397 # Then cache and return results
398 setattr(self, networks_attr, private_networks)
399 setattr(self, weights_attr, weights)
400 return private_networks, weights
402 def _get_public_networks_and_weights(
403 self,
404 address_class: Optional[str] = None,
405 ) -> Tuple[List[IPv4Network], List[int]]:
406 """
407 Produces a 2-tuple of valid public IPv4 networks and corresponding relative weights
409 :param address_class: IPv4 address class (a, b, or c)
410 """
411 # If `address_class` has an unexpected value, choose a valid value at random
412 if address_class not in _IPv4Constants._network_classes.keys():
413 address_class = self.ipv4_network_class()
415 # Return cached network and weight data if available for a specific address class
416 networks_attr = f"_cached_public_class_{address_class}_networks"
417 weights_attr = f"{networks_attr}_weights"
418 if hasattr(self, networks_attr) and hasattr(self, weights_attr):
419 return getattr(self, networks_attr), getattr(self, weights_attr)
421 # Otherwise, compute for list of public networks (excluding private and special networks)
422 public_networks = [_IPv4Constants._network_classes[address_class]] # type: ignore
423 public_networks = self._exclude_ipv4_networks(
424 public_networks,
425 _IPv4Constants._private_networks + _IPv4Constants._excluded_networks,
426 )
428 # Then compute for list of corresponding relative weights
429 weights = [network.num_addresses for network in public_networks]
431 # Then cache and return results
432 setattr(self, networks_attr, public_networks)
433 setattr(self, weights_attr, weights)
434 return public_networks, weights
436 def _random_ipv4_address_from_subnets(
437 self,
438 subnets: List[IPv4Network],
439 weights: Optional[List[int]] = None,
440 network: bool = False,
441 ) -> str:
442 """
443 Produces a random IPv4 address or network with a valid CIDR
444 from within the given subnets using a distribution described
445 by weights.
447 :param subnets: List of IPv4Networks to choose from within
448 :param weights: List of weights corresponding to the individual IPv4Networks
449 :param network: Return a network address, and not an IP address
450 :return:
451 """
452 if not subnets:
453 raise ValueError("No subnets to choose from")
455 # If the weights argument has an invalid value, default to equal distribution
456 if (
457 isinstance(weights, list)
458 and len(subnets) == len(weights)
459 and all(isinstance(w, (float, int)) for w in weights)
460 ):
461 subnet = choices_distribution(
462 subnets,
463 [float(w) for w in weights],
464 random=self.generator.random,
465 length=1,
466 )[0]
467 else:
468 subnet = self.generator.random.choice(subnets)
470 address = str(
471 subnet[
472 self.generator.random.randint(
473 0,
474 subnet.num_addresses - 1,
475 )
476 ],
477 )
479 if network:
480 address += "/" + str(
481 self.generator.random.randint(
482 subnet.prefixlen,
483 subnet.max_prefixlen,
484 )
485 )
486 address = str(IPv4Network(address, strict=False))
488 return address
490 def _exclude_ipv4_networks(
491 self, networks: List[IPv4Network], networks_to_exclude: List[IPv4Network]
492 ) -> List[IPv4Network]:
493 """
494 Exclude the list of networks from another list of networks
495 and return a flat list of new networks.
497 :param networks: List of IPv4 networks to exclude from
498 :param networks_to_exclude: List of IPv4 networks to exclude
499 :returns: Flat list of IPv4 networks
500 """
501 networks_to_exclude.sort(key=lambda x: x.prefixlen)
502 for network_to_exclude in networks_to_exclude:
504 def _exclude_ipv4_network(network):
505 """
506 Exclude a single network from another single network
507 and return a list of networks. Network to exclude
508 comes from the outer scope.
510 :param network: Network to exclude from
511 :returns: Flat list of IPv4 networks after exclusion.
512 If exclude fails because networks do not
513 overlap, a single element list with the
514 orignal network is returned. If it overlaps,
515 even partially, the network is excluded.
516 """
517 try:
518 return list(network.address_exclude(network_to_exclude))
519 except ValueError:
520 # If networks overlap partially, `address_exclude`
521 # will fail, but the network still must not be used
522 # in generation.
523 if network.overlaps(network_to_exclude):
524 return []
525 else:
526 return [network]
528 nested_networks = list(map(_exclude_ipv4_network, networks))
529 networks = [item for nested in nested_networks for item in nested]
531 return networks
533 def ipv4_network_class(self) -> str:
534 """
535 Returns a IPv4 network class 'a', 'b' or 'c'.
537 :returns: IPv4 network class
538 """
539 return self.random_element("abc")
541 def ipv4(
542 self,
543 network: bool = False,
544 address_class: Optional[str] = None,
545 private: Optional[str] = None,
546 ) -> str:
547 """
548 Returns a random IPv4 address or network with a valid CIDR.
550 :param network: Network address
551 :param address_class: IPv4 address class (a, b, or c)
552 :param private: Public or private
553 :returns: IPv4
554 """
555 if private is True:
556 return self.ipv4_private(address_class=address_class, network=network)
557 elif private is False:
558 return self.ipv4_public(address_class=address_class, network=network)
559 else:
560 all_networks, weights = self._get_all_networks_and_weights(address_class=address_class)
561 return self._random_ipv4_address_from_subnets(all_networks, weights=weights, network=network)
563 def ipv4_private(self, network: bool = False, address_class: Optional[str] = None) -> str:
564 """
565 Returns a private IPv4.
567 :param network: Network address
568 :param address_class: IPv4 address class (a, b, or c)
569 :returns: Private IPv4
570 """
571 private_networks, weights = self._get_private_networks_and_weights(address_class=address_class)
572 return self._random_ipv4_address_from_subnets(private_networks, weights=weights, network=network)
574 def ipv4_public(self, network: bool = False, address_class: Optional[str] = None) -> str:
575 """
576 Returns a public IPv4 excluding private blocks.
578 :param network: Network address
579 :param address_class: IPv4 address class (a, b, or c)
580 :returns: Public IPv4
581 """
582 public_networks, weights = self._get_public_networks_and_weights(address_class=address_class)
583 return self._random_ipv4_address_from_subnets(public_networks, weights=weights, network=network)
585 def ipv6(self, network: bool = False) -> str:
586 """Produce a random IPv6 address or network with a valid CIDR"""
587 address = str(IPv6Address(self.generator.random.randint(2**IPV4LENGTH, (2**IPV6LENGTH) - 1)))
588 if network:
589 address += "/" + str(self.generator.random.randint(0, IPV6LENGTH))
590 address = str(IPv6Network(address, strict=False))
591 return address
593 def mac_address(self) -> str:
594 mac = [self.generator.random.randint(0x00, 0xFF) for _ in range(0, 6)]
595 return ":".join("%02x" % x for x in mac)
597 def port_number(self, is_system: bool = False, is_user: bool = False, is_dynamic: bool = False) -> int:
598 """Returns a network port number
599 https://tools.ietf.org/html/rfc6335
601 :param is_system: System or well-known ports
602 :param is_user: User or registered ports
603 :param is_dynamic: Dynamic / private / ephemeral ports
604 :rtype: int
605 """
607 if is_system:
608 return self.random_int(min=0, max=1023)
609 elif is_user:
610 return self.random_int(min=1024, max=49151)
611 elif is_dynamic:
612 return self.random_int(min=49152, max=65535)
614 return self.random_int(min=0, max=65535)
616 def uri_page(self) -> str:
617 return self.random_element(self.uri_pages)
619 def uri_path(self, deep: Optional[int] = None) -> str:
620 deep = deep if deep else self.generator.random.randint(1, 3)
621 return "/".join(
622 self.random_elements(self.uri_paths, length=deep),
623 )
625 def uri_extension(self) -> str:
626 return self.random_element(self.uri_extensions)
628 def uri(self) -> str:
629 pattern: str = self.random_element(self.uri_formats)
630 return self.generator.parse(pattern)
632 @slugify
633 def slug(self, value: Optional[str] = None) -> str:
634 """Django algorithm"""
635 if value is None:
636 value = self.generator.text(20)
637 return value
639 def image_url(
640 self,
641 width: Optional[int] = None,
642 height: Optional[int] = None,
643 placeholder_url: Optional[str] = None,
644 ) -> str:
645 """
646 Returns URL to placeholder image
647 Example: http://placehold.it/640x480
649 :param width: Optional image width
650 :param height: Optional image height
651 :param placeholder_url: Optional template string of image URLs from custom
652 placeholder service. String must contain ``{width}`` and ``{height}``
653 placeholders, eg: ``https:/example.com/{width}/{height}``.
654 :rtype: str
655 """
656 width_ = width or self.random_int(max=1024)
657 height_ = height or self.random_int(max=1024)
658 if placeholder_url is None:
659 placeholder_url = self.random_element(self.image_placeholder_services)
660 return placeholder_url.format(width=width_, height=height_)
662 def iana_id(self) -> str:
663 """Returns IANA Registrar ID
664 https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml
666 :rtype: str
667 """
669 return str(self.random_int(min=1, max=8888888))
671 def ripe_id(self) -> str:
672 """Returns RIPE Organization ID
673 https://www.ripe.net/manage-ips-and-asns/db/support/organisation-object-in-the-ripe-database
675 :rtype: str
676 """
678 lex = "?" * self.random_int(min=2, max=4)
679 num = "%" * self.random_int(min=1, max=5)
680 return self.bothify(f"ORG-{lex}{num}-RIPE").upper()
682 def nic_handle(self, suffix: str = "FAKE") -> str:
683 """Returns NIC Handle ID
684 https://www.apnic.net/manage-ip/using-whois/guide/person/
686 :rtype: str
687 """
689 if len(suffix) < 2:
690 raise ValueError("suffix length must be greater than or equal to 2")
692 lex = "?" * self.random_int(min=2, max=4)
693 num = "%" * self.random_int(min=1, max=5)
694 return self.bothify(f"{lex}{num}-{suffix}").upper()
696 def nic_handles(self, count: int = 1, suffix: str = "????") -> List[str]:
697 """Returns NIC Handle ID list
699 :rtype: list[str]
700 """
702 return [self.nic_handle(suffix=suffix) for _ in range(count)]