|
| 1 | +import functools |
1 | 2 | import sys |
2 | 3 | import warnings |
3 | 4 | from collections.abc import Mapping, Sequence |
@@ -453,10 +454,7 @@ def host(self): |
453 | 454 | # fe80::2%Проверка |
454 | 455 | # presence of '%' sign means only IPv6 address, so idna is useless. |
455 | 456 | return raw |
456 | | - try: |
457 | | - return idna.decode(raw.encode("ascii")) |
458 | | - except UnicodeError: # e.g. '::1' |
459 | | - return raw.encode("ascii").decode("idna") |
| 457 | + return _idna_decode(raw) |
460 | 458 |
|
461 | 459 | @property |
462 | 460 | def port(self): |
@@ -671,12 +669,11 @@ def _encode_host(cls, host): |
671 | 669 | except ValueError: |
672 | 670 | # IDNA encoding is slow, |
673 | 671 | # skip it for ASCII-only strings |
| 672 | + # Don't move the check into _idna_encode() helper |
| 673 | + # to reduce the cache size |
674 | 674 | if host.isascii(): |
675 | 675 | return host |
676 | | - try: |
677 | | - host = idna.encode(host, uts46=True).decode("ascii") |
678 | | - except UnicodeError: |
679 | | - host = host.encode("idna").decode("ascii") |
| 676 | + return _idna_encode(host) |
680 | 677 | else: |
681 | 678 | host = ip.compressed |
682 | 679 | if sep: |
@@ -1029,3 +1026,41 @@ def human_repr(self): |
1029 | 1026 | self.fragment, |
1030 | 1027 | ) |
1031 | 1028 | ) |
| 1029 | + |
| 1030 | + |
| 1031 | +_MAXCACHE = 256 |
| 1032 | + |
| 1033 | + |
| 1034 | +@functools.lru_cache(_MAXCACHE) |
| 1035 | +def _idna_decode(raw): |
| 1036 | + try: |
| 1037 | + return idna.decode(raw.encode("ascii")) |
| 1038 | + except UnicodeError: # e.g. '::1' |
| 1039 | + return raw.encode("ascii").decode("idna") |
| 1040 | + |
| 1041 | + |
| 1042 | +@functools.lru_cache(_MAXCACHE) |
| 1043 | +def _idna_encode(host): |
| 1044 | + try: |
| 1045 | + return idna.encode(host, uts46=True).decode("ascii") |
| 1046 | + except UnicodeError: |
| 1047 | + return host.encode("idna").decode("ascii") |
| 1048 | + |
| 1049 | + |
| 1050 | +def cache_clear(): |
| 1051 | + _idna_decode.cache_clear() |
| 1052 | + _idna_encode.cache_clear() |
| 1053 | + |
| 1054 | + |
| 1055 | +def cache_info(): |
| 1056 | + return { |
| 1057 | + "idna_encode": _idna_encode.cache_info(), |
| 1058 | + "idna_decode": _idna_decode.cache_info(), |
| 1059 | + } |
| 1060 | + |
| 1061 | + |
| 1062 | +def cache_configure(*, idna_encode_size=_MAXCACHE, idna_decode_size=_MAXCACHE): |
| 1063 | + global _idna_decode, _idna_encode |
| 1064 | + |
| 1065 | + _idna_encode = functools.lru_cache(idna_encode_size)(_idna_encode.__wrapped__) |
| 1066 | + _idna_decode = functools.lru_cache(idna_decode_size)(_idna_decode.__wrapped__) |
0 commit comments