1*e1fe3e4aSElliott Hughes""" 2*e1fe3e4aSElliott HughesConversion functions. 3*e1fe3e4aSElliott Hughes""" 4*e1fe3e4aSElliott Hughes 5*e1fe3e4aSElliott Hughes# adapted from the UFO spec 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughesdef convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()): 9*e1fe3e4aSElliott Hughes # gather known kerning groups based on the prefixes 10*e1fe3e4aSElliott Hughes firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups) 11*e1fe3e4aSElliott Hughes # Make lists of groups referenced in kerning pairs. 12*e1fe3e4aSElliott Hughes for first, seconds in list(kerning.items()): 13*e1fe3e4aSElliott Hughes if first in groups and first not in glyphSet: 14*e1fe3e4aSElliott Hughes if not first.startswith("public.kern1."): 15*e1fe3e4aSElliott Hughes firstReferencedGroups.add(first) 16*e1fe3e4aSElliott Hughes for second in list(seconds.keys()): 17*e1fe3e4aSElliott Hughes if second in groups and second not in glyphSet: 18*e1fe3e4aSElliott Hughes if not second.startswith("public.kern2."): 19*e1fe3e4aSElliott Hughes secondReferencedGroups.add(second) 20*e1fe3e4aSElliott Hughes # Create new names for these groups. 21*e1fe3e4aSElliott Hughes firstRenamedGroups = {} 22*e1fe3e4aSElliott Hughes for first in firstReferencedGroups: 23*e1fe3e4aSElliott Hughes # Make a list of existing group names. 24*e1fe3e4aSElliott Hughes existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys()) 25*e1fe3e4aSElliott Hughes # Remove the old prefix from the name 26*e1fe3e4aSElliott Hughes newName = first.replace("@MMK_L_", "") 27*e1fe3e4aSElliott Hughes # Add the new prefix to the name. 28*e1fe3e4aSElliott Hughes newName = "public.kern1." + newName 29*e1fe3e4aSElliott Hughes # Make a unique group name. 30*e1fe3e4aSElliott Hughes newName = makeUniqueGroupName(newName, existingGroupNames) 31*e1fe3e4aSElliott Hughes # Store for use later. 32*e1fe3e4aSElliott Hughes firstRenamedGroups[first] = newName 33*e1fe3e4aSElliott Hughes secondRenamedGroups = {} 34*e1fe3e4aSElliott Hughes for second in secondReferencedGroups: 35*e1fe3e4aSElliott Hughes # Make a list of existing group names. 36*e1fe3e4aSElliott Hughes existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys()) 37*e1fe3e4aSElliott Hughes # Remove the old prefix from the name 38*e1fe3e4aSElliott Hughes newName = second.replace("@MMK_R_", "") 39*e1fe3e4aSElliott Hughes # Add the new prefix to the name. 40*e1fe3e4aSElliott Hughes newName = "public.kern2." + newName 41*e1fe3e4aSElliott Hughes # Make a unique group name. 42*e1fe3e4aSElliott Hughes newName = makeUniqueGroupName(newName, existingGroupNames) 43*e1fe3e4aSElliott Hughes # Store for use later. 44*e1fe3e4aSElliott Hughes secondRenamedGroups[second] = newName 45*e1fe3e4aSElliott Hughes # Populate the new group names into the kerning dictionary as needed. 46*e1fe3e4aSElliott Hughes newKerning = {} 47*e1fe3e4aSElliott Hughes for first, seconds in list(kerning.items()): 48*e1fe3e4aSElliott Hughes first = firstRenamedGroups.get(first, first) 49*e1fe3e4aSElliott Hughes newSeconds = {} 50*e1fe3e4aSElliott Hughes for second, value in list(seconds.items()): 51*e1fe3e4aSElliott Hughes second = secondRenamedGroups.get(second, second) 52*e1fe3e4aSElliott Hughes newSeconds[second] = value 53*e1fe3e4aSElliott Hughes newKerning[first] = newSeconds 54*e1fe3e4aSElliott Hughes # Make copies of the referenced groups and store them 55*e1fe3e4aSElliott Hughes # under the new names in the overall groups dictionary. 56*e1fe3e4aSElliott Hughes allRenamedGroups = list(firstRenamedGroups.items()) 57*e1fe3e4aSElliott Hughes allRenamedGroups += list(secondRenamedGroups.items()) 58*e1fe3e4aSElliott Hughes for oldName, newName in allRenamedGroups: 59*e1fe3e4aSElliott Hughes group = list(groups[oldName]) 60*e1fe3e4aSElliott Hughes groups[newName] = group 61*e1fe3e4aSElliott Hughes # Return the kerning and the groups. 62*e1fe3e4aSElliott Hughes return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups) 63*e1fe3e4aSElliott Hughes 64*e1fe3e4aSElliott Hughes 65*e1fe3e4aSElliott Hughesdef findKnownKerningGroups(groups): 66*e1fe3e4aSElliott Hughes """ 67*e1fe3e4aSElliott Hughes This will find kerning groups with known prefixes. 68*e1fe3e4aSElliott Hughes In some cases not all kerning groups will be referenced 69*e1fe3e4aSElliott Hughes by the kerning pairs. The algorithm for locating groups 70*e1fe3e4aSElliott Hughes in convertUFO1OrUFO2KerningToUFO3Kerning will miss these 71*e1fe3e4aSElliott Hughes unreferenced groups. By scanning for known prefixes 72*e1fe3e4aSElliott Hughes this function will catch all of the prefixed groups. 73*e1fe3e4aSElliott Hughes 74*e1fe3e4aSElliott Hughes These are the prefixes and sides that are handled: 75*e1fe3e4aSElliott Hughes @MMK_L_ - side 1 76*e1fe3e4aSElliott Hughes @MMK_R_ - side 2 77*e1fe3e4aSElliott Hughes 78*e1fe3e4aSElliott Hughes >>> testGroups = { 79*e1fe3e4aSElliott Hughes ... "@MMK_L_1" : None, 80*e1fe3e4aSElliott Hughes ... "@MMK_L_2" : None, 81*e1fe3e4aSElliott Hughes ... "@MMK_L_3" : None, 82*e1fe3e4aSElliott Hughes ... "@MMK_R_1" : None, 83*e1fe3e4aSElliott Hughes ... "@MMK_R_2" : None, 84*e1fe3e4aSElliott Hughes ... "@MMK_R_3" : None, 85*e1fe3e4aSElliott Hughes ... "@MMK_l_1" : None, 86*e1fe3e4aSElliott Hughes ... "@MMK_r_1" : None, 87*e1fe3e4aSElliott Hughes ... "@MMK_X_1" : None, 88*e1fe3e4aSElliott Hughes ... "foo" : None, 89*e1fe3e4aSElliott Hughes ... } 90*e1fe3e4aSElliott Hughes >>> first, second = findKnownKerningGroups(testGroups) 91*e1fe3e4aSElliott Hughes >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3'] 92*e1fe3e4aSElliott Hughes True 93*e1fe3e4aSElliott Hughes >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3'] 94*e1fe3e4aSElliott Hughes True 95*e1fe3e4aSElliott Hughes """ 96*e1fe3e4aSElliott Hughes knownFirstGroupPrefixes = ["@MMK_L_"] 97*e1fe3e4aSElliott Hughes knownSecondGroupPrefixes = ["@MMK_R_"] 98*e1fe3e4aSElliott Hughes firstGroups = set() 99*e1fe3e4aSElliott Hughes secondGroups = set() 100*e1fe3e4aSElliott Hughes for groupName in list(groups.keys()): 101*e1fe3e4aSElliott Hughes for firstPrefix in knownFirstGroupPrefixes: 102*e1fe3e4aSElliott Hughes if groupName.startswith(firstPrefix): 103*e1fe3e4aSElliott Hughes firstGroups.add(groupName) 104*e1fe3e4aSElliott Hughes break 105*e1fe3e4aSElliott Hughes for secondPrefix in knownSecondGroupPrefixes: 106*e1fe3e4aSElliott Hughes if groupName.startswith(secondPrefix): 107*e1fe3e4aSElliott Hughes secondGroups.add(groupName) 108*e1fe3e4aSElliott Hughes break 109*e1fe3e4aSElliott Hughes return firstGroups, secondGroups 110*e1fe3e4aSElliott Hughes 111*e1fe3e4aSElliott Hughes 112*e1fe3e4aSElliott Hughesdef makeUniqueGroupName(name, groupNames, counter=0): 113*e1fe3e4aSElliott Hughes # Add a number to the name if the counter is higher than zero. 114*e1fe3e4aSElliott Hughes newName = name 115*e1fe3e4aSElliott Hughes if counter > 0: 116*e1fe3e4aSElliott Hughes newName = "%s%d" % (newName, counter) 117*e1fe3e4aSElliott Hughes # If the new name is in the existing group names, recurse. 118*e1fe3e4aSElliott Hughes if newName in groupNames: 119*e1fe3e4aSElliott Hughes return makeUniqueGroupName(name, groupNames, counter + 1) 120*e1fe3e4aSElliott Hughes # Otherwise send back the new name. 121*e1fe3e4aSElliott Hughes return newName 122*e1fe3e4aSElliott Hughes 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughesdef test(): 125*e1fe3e4aSElliott Hughes """ 126*e1fe3e4aSElliott Hughes No known prefixes. 127*e1fe3e4aSElliott Hughes 128*e1fe3e4aSElliott Hughes >>> testKerning = { 129*e1fe3e4aSElliott Hughes ... "A" : { 130*e1fe3e4aSElliott Hughes ... "A" : 1, 131*e1fe3e4aSElliott Hughes ... "B" : 2, 132*e1fe3e4aSElliott Hughes ... "CGroup" : 3, 133*e1fe3e4aSElliott Hughes ... "DGroup" : 4 134*e1fe3e4aSElliott Hughes ... }, 135*e1fe3e4aSElliott Hughes ... "BGroup" : { 136*e1fe3e4aSElliott Hughes ... "A" : 5, 137*e1fe3e4aSElliott Hughes ... "B" : 6, 138*e1fe3e4aSElliott Hughes ... "CGroup" : 7, 139*e1fe3e4aSElliott Hughes ... "DGroup" : 8 140*e1fe3e4aSElliott Hughes ... }, 141*e1fe3e4aSElliott Hughes ... "CGroup" : { 142*e1fe3e4aSElliott Hughes ... "A" : 9, 143*e1fe3e4aSElliott Hughes ... "B" : 10, 144*e1fe3e4aSElliott Hughes ... "CGroup" : 11, 145*e1fe3e4aSElliott Hughes ... "DGroup" : 12 146*e1fe3e4aSElliott Hughes ... }, 147*e1fe3e4aSElliott Hughes ... } 148*e1fe3e4aSElliott Hughes >>> testGroups = { 149*e1fe3e4aSElliott Hughes ... "BGroup" : ["B"], 150*e1fe3e4aSElliott Hughes ... "CGroup" : ["C"], 151*e1fe3e4aSElliott Hughes ... "DGroup" : ["D"], 152*e1fe3e4aSElliott Hughes ... } 153*e1fe3e4aSElliott Hughes >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( 154*e1fe3e4aSElliott Hughes ... testKerning, testGroups, []) 155*e1fe3e4aSElliott Hughes >>> expected = { 156*e1fe3e4aSElliott Hughes ... "A" : { 157*e1fe3e4aSElliott Hughes ... "A": 1, 158*e1fe3e4aSElliott Hughes ... "B": 2, 159*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 3, 160*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 4 161*e1fe3e4aSElliott Hughes ... }, 162*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": { 163*e1fe3e4aSElliott Hughes ... "A": 5, 164*e1fe3e4aSElliott Hughes ... "B": 6, 165*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 7, 166*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 8 167*e1fe3e4aSElliott Hughes ... }, 168*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": { 169*e1fe3e4aSElliott Hughes ... "A": 9, 170*e1fe3e4aSElliott Hughes ... "B": 10, 171*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 11, 172*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 12 173*e1fe3e4aSElliott Hughes ... } 174*e1fe3e4aSElliott Hughes ... } 175*e1fe3e4aSElliott Hughes >>> kerning == expected 176*e1fe3e4aSElliott Hughes True 177*e1fe3e4aSElliott Hughes >>> expected = { 178*e1fe3e4aSElliott Hughes ... "BGroup": ["B"], 179*e1fe3e4aSElliott Hughes ... "CGroup": ["C"], 180*e1fe3e4aSElliott Hughes ... "DGroup": ["D"], 181*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": ["B"], 182*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": ["C"], 183*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": ["C"], 184*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": ["D"], 185*e1fe3e4aSElliott Hughes ... } 186*e1fe3e4aSElliott Hughes >>> groups == expected 187*e1fe3e4aSElliott Hughes True 188*e1fe3e4aSElliott Hughes 189*e1fe3e4aSElliott Hughes Known prefixes. 190*e1fe3e4aSElliott Hughes 191*e1fe3e4aSElliott Hughes >>> testKerning = { 192*e1fe3e4aSElliott Hughes ... "A" : { 193*e1fe3e4aSElliott Hughes ... "A" : 1, 194*e1fe3e4aSElliott Hughes ... "B" : 2, 195*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 3, 196*e1fe3e4aSElliott Hughes ... "@MMK_R_DGroup" : 4 197*e1fe3e4aSElliott Hughes ... }, 198*e1fe3e4aSElliott Hughes ... "@MMK_L_BGroup" : { 199*e1fe3e4aSElliott Hughes ... "A" : 5, 200*e1fe3e4aSElliott Hughes ... "B" : 6, 201*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 7, 202*e1fe3e4aSElliott Hughes ... "@MMK_R_DGroup" : 8 203*e1fe3e4aSElliott Hughes ... }, 204*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup" : { 205*e1fe3e4aSElliott Hughes ... "A" : 9, 206*e1fe3e4aSElliott Hughes ... "B" : 10, 207*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 11, 208*e1fe3e4aSElliott Hughes ... "@MMK_R_DGroup" : 12 209*e1fe3e4aSElliott Hughes ... }, 210*e1fe3e4aSElliott Hughes ... } 211*e1fe3e4aSElliott Hughes >>> testGroups = { 212*e1fe3e4aSElliott Hughes ... "@MMK_L_BGroup" : ["B"], 213*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup" : ["C"], 214*e1fe3e4aSElliott Hughes ... "@MMK_L_XGroup" : ["X"], 215*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : ["C"], 216*e1fe3e4aSElliott Hughes ... "@MMK_R_DGroup" : ["D"], 217*e1fe3e4aSElliott Hughes ... "@MMK_R_XGroup" : ["X"], 218*e1fe3e4aSElliott Hughes ... } 219*e1fe3e4aSElliott Hughes >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( 220*e1fe3e4aSElliott Hughes ... testKerning, testGroups, []) 221*e1fe3e4aSElliott Hughes >>> expected = { 222*e1fe3e4aSElliott Hughes ... "A" : { 223*e1fe3e4aSElliott Hughes ... "A": 1, 224*e1fe3e4aSElliott Hughes ... "B": 2, 225*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 3, 226*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 4 227*e1fe3e4aSElliott Hughes ... }, 228*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": { 229*e1fe3e4aSElliott Hughes ... "A": 5, 230*e1fe3e4aSElliott Hughes ... "B": 6, 231*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 7, 232*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 8 233*e1fe3e4aSElliott Hughes ... }, 234*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": { 235*e1fe3e4aSElliott Hughes ... "A": 9, 236*e1fe3e4aSElliott Hughes ... "B": 10, 237*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 11, 238*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 12 239*e1fe3e4aSElliott Hughes ... } 240*e1fe3e4aSElliott Hughes ... } 241*e1fe3e4aSElliott Hughes >>> kerning == expected 242*e1fe3e4aSElliott Hughes True 243*e1fe3e4aSElliott Hughes >>> expected = { 244*e1fe3e4aSElliott Hughes ... "@MMK_L_BGroup": ["B"], 245*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup": ["C"], 246*e1fe3e4aSElliott Hughes ... "@MMK_L_XGroup": ["X"], 247*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup": ["C"], 248*e1fe3e4aSElliott Hughes ... "@MMK_R_DGroup": ["D"], 249*e1fe3e4aSElliott Hughes ... "@MMK_R_XGroup": ["X"], 250*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": ["B"], 251*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": ["C"], 252*e1fe3e4aSElliott Hughes ... "public.kern1.XGroup": ["X"], 253*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": ["C"], 254*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": ["D"], 255*e1fe3e4aSElliott Hughes ... "public.kern2.XGroup": ["X"], 256*e1fe3e4aSElliott Hughes ... } 257*e1fe3e4aSElliott Hughes >>> groups == expected 258*e1fe3e4aSElliott Hughes True 259*e1fe3e4aSElliott Hughes 260*e1fe3e4aSElliott Hughes >>> from .validators import kerningValidator 261*e1fe3e4aSElliott Hughes >>> kerningValidator(kerning) 262*e1fe3e4aSElliott Hughes (True, None) 263*e1fe3e4aSElliott Hughes 264*e1fe3e4aSElliott Hughes Mixture of known prefixes and groups without prefixes. 265*e1fe3e4aSElliott Hughes 266*e1fe3e4aSElliott Hughes >>> testKerning = { 267*e1fe3e4aSElliott Hughes ... "A" : { 268*e1fe3e4aSElliott Hughes ... "A" : 1, 269*e1fe3e4aSElliott Hughes ... "B" : 2, 270*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 3, 271*e1fe3e4aSElliott Hughes ... "DGroup" : 4 272*e1fe3e4aSElliott Hughes ... }, 273*e1fe3e4aSElliott Hughes ... "BGroup" : { 274*e1fe3e4aSElliott Hughes ... "A" : 5, 275*e1fe3e4aSElliott Hughes ... "B" : 6, 276*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 7, 277*e1fe3e4aSElliott Hughes ... "DGroup" : 8 278*e1fe3e4aSElliott Hughes ... }, 279*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup" : { 280*e1fe3e4aSElliott Hughes ... "A" : 9, 281*e1fe3e4aSElliott Hughes ... "B" : 10, 282*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : 11, 283*e1fe3e4aSElliott Hughes ... "DGroup" : 12 284*e1fe3e4aSElliott Hughes ... }, 285*e1fe3e4aSElliott Hughes ... } 286*e1fe3e4aSElliott Hughes >>> testGroups = { 287*e1fe3e4aSElliott Hughes ... "BGroup" : ["B"], 288*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup" : ["C"], 289*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup" : ["C"], 290*e1fe3e4aSElliott Hughes ... "DGroup" : ["D"], 291*e1fe3e4aSElliott Hughes ... } 292*e1fe3e4aSElliott Hughes >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning( 293*e1fe3e4aSElliott Hughes ... testKerning, testGroups, []) 294*e1fe3e4aSElliott Hughes >>> expected = { 295*e1fe3e4aSElliott Hughes ... "A" : { 296*e1fe3e4aSElliott Hughes ... "A": 1, 297*e1fe3e4aSElliott Hughes ... "B": 2, 298*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 3, 299*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 4 300*e1fe3e4aSElliott Hughes ... }, 301*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": { 302*e1fe3e4aSElliott Hughes ... "A": 5, 303*e1fe3e4aSElliott Hughes ... "B": 6, 304*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 7, 305*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 8 306*e1fe3e4aSElliott Hughes ... }, 307*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": { 308*e1fe3e4aSElliott Hughes ... "A": 9, 309*e1fe3e4aSElliott Hughes ... "B": 10, 310*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": 11, 311*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": 12 312*e1fe3e4aSElliott Hughes ... } 313*e1fe3e4aSElliott Hughes ... } 314*e1fe3e4aSElliott Hughes >>> kerning == expected 315*e1fe3e4aSElliott Hughes True 316*e1fe3e4aSElliott Hughes >>> expected = { 317*e1fe3e4aSElliott Hughes ... "BGroup": ["B"], 318*e1fe3e4aSElliott Hughes ... "@MMK_L_CGroup": ["C"], 319*e1fe3e4aSElliott Hughes ... "@MMK_R_CGroup": ["C"], 320*e1fe3e4aSElliott Hughes ... "DGroup": ["D"], 321*e1fe3e4aSElliott Hughes ... "public.kern1.BGroup": ["B"], 322*e1fe3e4aSElliott Hughes ... "public.kern1.CGroup": ["C"], 323*e1fe3e4aSElliott Hughes ... "public.kern2.CGroup": ["C"], 324*e1fe3e4aSElliott Hughes ... "public.kern2.DGroup": ["D"], 325*e1fe3e4aSElliott Hughes ... } 326*e1fe3e4aSElliott Hughes >>> groups == expected 327*e1fe3e4aSElliott Hughes True 328*e1fe3e4aSElliott Hughes """ 329*e1fe3e4aSElliott Hughes 330*e1fe3e4aSElliott Hughes 331*e1fe3e4aSElliott Hughesif __name__ == "__main__": 332*e1fe3e4aSElliott Hughes import doctest 333*e1fe3e4aSElliott Hughes 334*e1fe3e4aSElliott Hughes doctest.testmod() 335