codec for the punicode encoding as specified in rfc 3492 written by martin v lwis encoding 3 1 basic code point segregation base bytearray extended set for c in str if ordc 128 base appendordc else extended addc extended sortedextended return bytesbase extended def selectivelenstr max return a pair index pos indicating the next occurrence of char in str index is the position of the character considering only ordinals up to and including char and pos is the position in the full string indexpos is the starting position in the full string l lenstr while 1 pos 1 if pos l return 1 1 c strpos if c char return index1 pos elif c char index 1 def insertionunsortstr extended punycode parameters tmin 1 tmax 26 base 36 3 3 generalized variablelength integers result bytearray j 0 while 1 t tj bias if n t result appenddigitsn return bytesresult result appenddigitst n t 36 t n n t 36 t j 1 def adaptdelta first numchars if first delta 700 else delta 2 delta delta numchars base tmin tmax 2 455 divisions 0 while delta 455 delta delta 35 base tmin divisions 36 bias divisions 36 delta delta 38 return bias def generateintegersbaselen deltas punycode parameters initial bias 72 damp 700 skew 38 decoding 3 3 generalized variablelength integers result 0 w 1 j 0 while 1 try char ordextendedextpos except indexerror if errors strict raise unicodeerrorincomplete punicode string return extpos 1 none extpos 1 if 0x41 char 0x5a az digit char 0x41 elif 0x30 char 0x39 digit char 22 0x3026 elif errors strict raise unicodeerrorinvalid extended code point s extendedextpos1 else return extpos none t tj bias result digit w if digit t return extpos result w w 36 t j 1 def insertionsortbase extended errors there was an error in decoding we can t continue because synchronization is lost codec apis encodings module api encoding 3 1 basic code point segregation return the length of str considering only characters below max return a pair index pos indicating the next occurrence of char in str index is the position of the character considering only ordinals up to and including char and pos is the position in the full string index pos is the starting position in the full string 3 2 insertion unsort coding punycode parameters tmin 1 tmax 26 base 36 3 3 generalized variable length integers base tmin tmax 2 455 base tmin 3 4 bias adaptation punycode parameters initial bias 72 damp 700 skew 38 decoding 3 3 generalized variable length integers a z 0x30 26 3 2 insertion unsort coding there was an error in decoding we can t continue because synchronization is lost codec apis encodings module api
import codecs



def segregate(str):
    
    base = bytearray()
    extended = set()
    for c in str:
        if ord(c) < 128:
            base.append(ord(c))
        else:
            extended.add(c)
    extended = sorted(extended)
    return bytes(base), extended

def selective_len(str, max):
    
    res = 0
    for c in str:
        if ord(c) < max:
            res += 1
    return res

def selective_find(str, char, index, pos):
    

    l = len(str)
    while 1:
        pos += 1
        if pos == l:
            return (-1, -1)
        c = str[pos]
        if c == char:
            return index+1, pos
        elif c < char:
            index += 1

def insertion_unsort(str, extended):
    
    oldchar = 0x80
    result = []
    oldindex = -1
    for c in extended:
        index = pos = -1
        char = ord(c)
        curlen = selective_len(str, char)
        delta = (curlen+1) * (char - oldchar)
        while 1:
            index,pos = selective_find(str,c,index,pos)
            if index == -1:
                break
            delta += index - oldindex
            result.append(delta-1)
            oldindex = index
            delta = 0
        oldchar = char

    return result

def T(j, bias):
    
    res = 36 * (j + 1) - bias
    if res < 1: return 1
    if res > 26: return 26
    return res

digits = b"abcdefghijklmnopqrstuvwxyz0123456789"
def generate_generalized_integer(N, bias):
    
    result = bytearray()
    j = 0
    while 1:
        t = T(j, bias)
        if N < t:
            result.append(digits[N])
            return bytes(result)
        result.append(digits[t + ((N - t) % (36 - t))])
        N = (N - t) // (36 - t)
        j += 1

def adapt(delta, first, numchars):
    if first:
        delta //= 700
    else:
        delta //= 2
    delta += delta // numchars
    
    divisions = 0
    while delta > 455:
        delta = delta // 35 
        divisions += 36
    bias = divisions + (36 * delta // (delta + 38))
    return bias


def generate_integers(baselen, deltas):
    
    
    result = bytearray()
    bias = 72
    for points, delta in enumerate(deltas):
        s = generate_generalized_integer(delta, bias)
        result.extend(s)
        bias = adapt(delta, points==0, baselen+points+1)
    return bytes(result)

def punycode_encode(text):
    base, extended = segregate(text)
    deltas = insertion_unsort(text, extended)
    extended = generate_integers(len(base), deltas)
    if base:
        return base + b"-" + extended
    return extended



def decode_generalized_number(extended, extpos, bias, errors):
    
    result = 0
    w = 1
    j = 0
    while 1:
        try:
            char = ord(extended[extpos])
        except IndexError:
            if errors == "strict":
                raise UnicodeError("incomplete punicode string")
            return extpos + 1, None
        extpos += 1
        if 0x41 <= char <= 0x5A: 
            digit = char - 0x41
        elif 0x30 <= char <= 0x39:
            digit = char - 22 
        elif errors == "strict":
            raise UnicodeError("Invalid extended code point '%s'"
                               % extended[extpos-1])
        else:
            return extpos, None
        t = T(j, bias)
        result += digit * w
        if digit < t:
            return extpos, result
        w = w * (36 - t)
        j += 1


def insertion_sort(base, extended, errors):
    
    char = 0x80
    pos = -1
    bias = 72
    extpos = 0
    while extpos < len(extended):
        newpos, delta = decode_generalized_number(extended, extpos,
                                                  bias, errors)
        if delta is None:
            
            
            return base
        pos += delta+1
        char += pos // (len(base) + 1)
        if char > 0x10FFFF:
            if errors == "strict":
                raise UnicodeError("Invalid character U+%x" % char)
            char = ord('?')
        pos = pos % (len(base) + 1)
        base = base[:pos] + chr(char) + base[pos:]
        bias = adapt(delta, (extpos == 0), len(base))
        extpos = newpos
    return base

def punycode_decode(text, errors):
    if isinstance(text, str):
        text = text.encode("ascii")
    if isinstance(text, memoryview):
        text = bytes(text)
    pos = text.rfind(b"-")
    if pos == -1:
        base = ""
        extended = str(text, "ascii").upper()
    else:
        base = str(text[:pos], "ascii", errors)
        extended = str(text[pos+1:], "ascii").upper()
    return insertion_sort(base, extended, errors)



class Codec(codecs.Codec):

    def encode(self, input, errors='strict'):
        res = punycode_encode(input)
        return res, len(input)

    def decode(self, input, errors='strict'):
        if errors not in ('strict', 'replace', 'ignore'):
            raise UnicodeError("Unsupported error handling "+errors)
        res = punycode_decode(input, errors)
        return res, len(input)

class IncrementalEncoder(codecs.IncrementalEncoder):
    def encode(self, input, final=False):
        return punycode_encode(input)

class IncrementalDecoder(codecs.IncrementalDecoder):
    def decode(self, input, final=False):
        if self.errors not in ('strict', 'replace', 'ignore'):
            raise UnicodeError("Unsupported error handling "+self.errors)
        return punycode_decode(input, self.errors)

class StreamWriter(Codec,codecs.StreamWriter):
    pass

class StreamReader(Codec,codecs.StreamReader):
    pass



def getregentry():
    return codecs.CodecInfo(
        name='punycode',
        encode=Codec().encode,
        decode=Codec().decode,
        incrementalencoder=IncrementalEncoder,
        incrementaldecoder=IncrementalDecoder,
        streamwriter=StreamWriter,
        streamreader=StreamReader,
    )