codekingpro/portable-devtools
115k
1# Copyright 2014 Mathias Bynens <https://mathiasbynens.be/>2# Copyright 2021 Taneli Hukkinen3#4# Permission is hereby granted, free of charge, to any person obtaining5# a copy of this software and associated documentation files (the6# "Software"), to deal in the Software without restriction, including7# without limitation the rights to use, copy, modify, merge, publish,8# distribute, sublicense, and/or sell copies of the Software, and to9# permit persons to whom the Software is furnished to do so, subject to10# the following conditions:11#12# The above copyright notice and this permission notice shall be13# included in all copies or substantial portions of the Software.14#15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,16# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF17# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE19# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION20# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION21# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.22 23import codecs24from collections.abc import Callable25import re26 27REGEX_SEPARATORS = re.compile(r"[\x2E\u3002\uFF0E\uFF61]")28REGEX_NON_ASCII = re.compile(r"[^\0-\x7E]")29 30 31def encode(uni: str) -> str:32 return codecs.encode(uni, encoding="punycode").decode()33 34 35def decode(ascii: str) -> str:36 return codecs.decode(ascii, encoding="punycode") # type: ignore37 38 39def map_domain(string: str, fn: Callable[[str], str]) -> str:40 parts = string.split("@")41 result = ""42 if len(parts) > 1:43 # In email addresses, only the domain name should be punycoded. Leave44 # the local part (i.e. everything up to `@`) intact.45 result = parts[0] + "@"46 string = parts[1]47 labels = REGEX_SEPARATORS.split(string)48 encoded = ".".join(fn(label) for label in labels)49 return result + encoded50 51 52def to_unicode(obj: str) -> str:53 def mapping(obj: str) -> str:54 if obj.startswith("xn--"):55 return decode(obj[4:].lower())56 return obj57 58 return map_domain(obj, mapping)59 60 61def to_ascii(obj: str) -> str:62 def mapping(obj: str) -> str:63 if REGEX_NON_ASCII.search(obj):64 return "xn--" + encode(obj)65 return obj66 67 return map_domain(obj, mapping)68 