Over the years I’ve written quite a few python utilities. Most of them are really short, and too specific to be useful to others. Those that aren’t are so long that they don’t feel elegant enough to share.
And then, between these two extremes, are the gems that I find myself coming back to time and again. I thought I’d share these with you. Keep in mind that all of these are covered by the Paul Spooner “IP is Wicked Nonsense” license, and you are encouraged to evaluate them on their own merits as distinct from their history. If you insist that the origins of ideas are important, then go pledge your support to my efforts.
TextOddifier.py is a text manipulation script to convert letters in UTF-8 to fairly similar looking letters. It makes text that looks like this ḽӧœĸ ŀįҟԙ ҭԡ༑ṩ ѩʼnᵴťӕаԂ. You can find the source below, or through the link at the start of the description.
# Similar letter substituter # To make text look a bit strange from random import choice SLD = { 'A':'ĀĂĄАѦѨӐӒӔḀẲⰡ⍲ᗗ','a':'āăąаѧѩӑӓӕḁẳ⎀', 'B':'ВѢҌ฿ḂḄḆᙗᙫ','b':'ЉЊБЪЬъыьѣҍḃḅḇᒀᖲ', 'C':'ĆĊČСҪℂ℃','c':'ćĉċčсҫ', 'D':'ĎĐḊḌḎḐḒ⫐⫒ᗫᗤ','d':'ďđԀԁԂԃḓḑḏḍḋᑽᖱ', 'E':'ĒĔĖĘĚŒЀЁЄЕѤӔӖԘ⋵⋶ᕮ','e':'ēĕėęěœеѐёѥӕӗԙ', 'F':'ҒӺḞ℉ᖴ','f':'ſғӻẛẜẝḟ', 'G':'ĜĞĠĢԌḠ','g':'ĝğġģḡ', 'H':'ĤĦЊНҔҢҤӇӉԊԢԨԪ','h':'ĥħЂЋђћҕңҥӈӊԡԦԧℎℏ', 'I':'ĨĪĬİIJІЇЮѤѨӀ','i':'ĩīĭįıijіїѩ༐༑', 'J':'IJĴЈЉӇԈԒԔᒏ','j':'ijĵЛјӈԓԕ', 'K':'ĶЌЖКҖҚҜҞҠӃԖԞԪ','k':'ķĸкҗқҝҟҡӄԗԟ', 'L':'ĹĻĽĿŁḶḸḺḼᒺ','l':'ĺļľŀłӏḷḹḻḽ', 'M':'МӍᴍḾṀṂ','m':'ӎᵚᵯmṃṁḿ', 'N':'ŃŅŇŊЍИЙҊӢӤ','n':'ńņňʼnŋҋᵰ', 'O':'ŌŎŐŒОЮѺӦ⥀⥁','o':'ōŏőœбоѻӧ', 'P':'РҎԖⰒⰐᑬᑭᑮᑶᑸ','p':'рҏԗᵱⱀᖰ', 'Q':'ҀԚⰢ','q':'ҁԛⱉᖳ', 'R':'ŔŖŘЯԄԆԖԘ℞ᖇ','r':'ŕŗřѓг', 'S':'ŚŜŞŠЅṦṨᔡ','s':'śŝşšѕᵴṩṧṥ', 'T':'ŢŤŦЂЃЋГТѢѢҬԎ','t':'ţťŧҭԏ', 'U':'ŨŪŬŮŰŲ⩂⩁⋃ᙈᙀ','u':'ũūŭůűų', 'V':'ѴѶṼṾⰜ⩢⩣⩛⩝⍱⋁ᐻ','v':'ѵѷṿṽⱌ⩡', 'W':'ŴѠѼѾԜ','w':'ŵѡѽѿԝ⍹', 'X':'ЖХҖӁӜӼӾԔ','x':'жхҗӂӝӽӿԕẋ', 'Y':'ŶŸЎУҮӮӰӲ','y':'ŷуўӯӱӳ', 'Z':'ŹŻŽᤏᤁ','z':'źżžᙇ', '.':'.․⁝⁞', '?':'?‽⁇⁈', '!':'!‽⁉', } def oddify(text): result = "" for i in text: try: result += choice(SLD[i]) except: result += i return result s = input("enter the text you want to oddify: ") #s = "I don't even really know how this is going to work any more" print(oddify(s)) input("Press enter when done:")
BaseConverter_py.py is a number base converter that we collaborated on to convert numbers to and from arbitrary radix. The neat thing about it is it works on floating point numbers as well as integers. The example (and test case) is 135.5 converted to base 12 is B3.6. Going the other way around, S4 in base 35 is 984 in decimal! You can find the source below, or through the link at the start of the description.
#!python # dozinal.py BASE = 12 PREC = 8 GLYPHS = {} VALUES = {} def compute_glyphs(): global GLYPHS, VALUES GLYPHS = {} VALUES = {} for i in range(BASE): if i < 10: GLYPHS[i] = str(i) else: GLYPHS[i] = chr(i+55) for i in range(len(GLYPHS)): VALUES[GLYPHS[i]] = i def rebase(num): if len(GLYPHS) != BASE: compute_glyphs() if num < 0: sign = '-' num = -num else: sign = '' whole = int(num) frac = num - whole whole_parts = [] frac_parts = [] prec = PREC while prec > 0: prec -= 1 frac *= BASE part = int(frac) frac -= part if part == 0: break frac_parts.append(GLYPHS[part]) while whole > 0: mod = whole % BASE whole = whole // BASE whole_parts.append(GLYPHS[mod]) if len(whole_parts) == 0: whole_parts.append(GLYPHS[0]) if len(frac_parts) == 0: return sign + "".join(reversed(whole_parts)) return sign + "{}.{}".format("".join(reversed(whole_parts)), "".join(frac_parts)) def debase(string): if len(GLYPHS) != BASE: compute_glyphs() if string[0] == '-': negative = True string = string[1:] else: negative = False try: whole_parts, frac_parts = string.split(".") except: whole_parts = string frac_parts = '' whole = 0 for idx in range(len(whole_parts)): whole += VALUES[whole_parts[idx]] if idx < len(whole_parts) - 1: whole *= BASE frac = 0 max_idx = len(frac_parts) - 1 for idx in range(len(frac_parts)): part = frac_parts[max_idx - idx] frac += VALUES[part] / BASE if idx < max_idx: frac /= BASE result = whole + round(frac,PREC) if negative: return -result return result TestConvert = 135.5 TestDozenal = rebase(TestConvert) print("{} converted to base {} is".format(TestConvert,BASE), TestDozenal) ConvertedBack = debase(TestDozenal) print("{} in base {} is".format(TestDozenal,BASE), ConvertedBack, "in decimal") if TestConvert == ConvertedBack: print("success!") else: print("Something went wrong") print("Assign BASE to set the number base,\nand PREC to set the precision.\nCall rebase() to convert,\nand debase() to convert back to decimal.")
Renamer.py Is a short program I wrote to do simple renaming operations on files. It only operates on the files in the folder, so it’s pretty easy to target. I include it here mostly as a syntax reminder. Change “rename” to “renames” to create directories (folders), which are separated by a forward slash.
# Rename files to add "_" from os import rename, listdir thesefiles = listdir() target = "Prefix" tlen = len(target) for f in thesefiles: if f[:tlen] == target: n = f n = n[:tlen] + '_' + n[tlen:] rename(f, n) print(n)
mcm_page.py is kind of an odd one. I really like the McMaster-Carr website, and enjoy browsing their catalog for inspiration. Time was when the catalog was paper that you could turn to a random page and peruse it. However, the online catalog is so efficient at delivering what you want that this becomes difficult. I’d also like to ensure that I don’t keep seeing the same page over again, at least until I’ve gone through the whole catalog. This script does all of that, opening a random page of the catalog when you start it, and allowing a save file listing all the pages you haven’t visited yet. Could be easily modified for other things… monte-carlo webcomic binges for example.
# opens a random McMaster Carr catalog page from random import choice import webbrowser # 3873 fetched on 2017-09-20 # 3939 fetched on 2018-11-29, catalog 124 # 4061 fetched on 2020-11-17, catalog 126 HIGHEST_PAGE_NUMBER = 4061 try: f = open('mcm_page_numbers.txt','r') maxpgraw = f.readline() raw = f.readline() f.close() maxpg = int(maxpgraw) rem_pgs = eval(raw) if maxpg == HIGHEST_PAGE_NUMBER: pass elif maxpg > HIGHEST_PAGE_NUMBER: while len(rem_pgs)>0: if rem_pgs[-1] > HIGHEST_PAGE_NUMBER: rem_pgs.pop() else: break elif maxpg < HIGHEST_PAGE_NUMBER: for i in range(maxpg,HIGHEST_PAGE_NUMBER+1): rem_pgs.append(i) else: print("Something went terribly wrong") except: rem_pgs = [i for i in range(1,HIGHEST_PAGE_NUMBER+1)] inchoice = "" print('{} pages left'.format(len(rem_pgs))) print('Enter to bring up a McMaster page\n"s" to save, "sc" to save and close, "p" to print') while len(rem_pgs) > 0: printflag = False if len(inchoice)!= 0: inchoice = inchoice.lower() initial = inchoice[0] if initial == 's': print("Saving") f = open('mcm_page_numbers.txt','w') f.write(str(HIGHEST_PAGE_NUMBER)+'\n') f.write(str(rem_pgs)) f.close() if inchoice == 'sc': break else: print('{} pages left'.format(len(rem_pgs))) inchoice = input('Saved :') continue elif initial == 'p': print(rem_pgs) inchoice = input('Those are the currently loaded indicies :') continue idx = choice(range(len(rem_pgs))) chosen_page = rem_pgs.pop(idx) webbrowser.open('https://www.mcmaster.com/catalog/126/{}'.format(chosen_page)) inchoice = input('Page {} queued:'.format(chosen_page)) input("There are no more pages. Press return to close.")
NatoPhoneticAlphabet.py Is a dumb little thing that converts characters into their equivalent in the NATO Phonetic Alphabet. More of a fun toy than a useful tool.
#NATO Phonetic Alphabetizer SLD = { 'A':'Alfa', 'B':'Bravo', 'C':'Charlie', 'D':'Delta', 'E':'Echo', 'F':'Foxtrot', 'G':'Golf', 'H':'Hotel', 'I':'India', 'J':'Juliet', 'K':'Kilo', 'L':'Lima', 'M':'Mike', 'N':'November', 'O':'Oscar', 'P':'Papa', 'Q':'Quebec', 'R':'Romeo', 'S':'Sierra', 'T':'Tango', 'U':'Uniform', 'V':'Victor', 'W':'Whiskey', 'X':'X-Ray', 'Y':'Yankee', 'Z':'Zulu', '9':'9er', '0':'Zero', } def phoneticize(text): result = [] for i in text.upper(): try: result += [SLD[i]] except: result += [i] return " ".join(result) while(True): s = input("enter text: ") #s = "I don't even really know how this is going to work any more" print(phoneticize(s))
I have enjoyed playing with the Fantasy Genesis book for several years now, and at one point wrote this python script to cut out all the dice rolling.
# A Fantasy Genesis Nested Lists # The Lists: # Anima Anim_1_Sea_Life = ["Mollusk", "Crab, Lobster", "Squid, Mudskipper", ["Fish: Deep sea", "Fish: Fresh water"], "Jellyfish", [["Whale", "Killer Whale", "Narwhale"], "Dolphin", "Walrus", "Otter"], ["Shell: spiral", "Conch", "Shell: ribbed, clam"], "Eel, Leech, Hagfish", "Coral, Anemone", "Shark, ray"] Anim_2_Insect = ["Worm", "Ant", "Mosquito", "Moth, Butterfly", "Fly, dragonfly", "Lotus, Mantis", "Bee, Wasp", "Caterpillar, Centipede, Milipede", "Beetle, Scarab", "Flea, Mite", "Spider"] Anim_3_Mammal = ["Sheep, cow", "Mouse, Rabbit", "Pig, Boar", "Deer, Pronghorn", "Ram, Bull, Buck", "Elephant, Giraffe", "Bat", "Bear", "Lupine: Wild Dog", "Horse, Zeebra", "Feline: Wild Cat", "Primate"] Anim_4_Reptile = ["Crocodile, Gila", "Frog, Newt", "Lizard, Snake", "Turtle"] Anim_5_Bird = ["Wild Fowl, Duck, Goose", "Farm Fowl, Rooster", "Seabird, Penguin, Gull", "City bird: Raven, Sparrow", "Tropical bird: Parrot, Heron", "Bird of Prey: Hawk, Owl", "Hummingbird"] # Veggie Vegi_1_Plant = ["Seaweed", "Fern", "Desert Cacti", "Thick Leaf Plant, Jade", "Flower: Domestic", "Vine", "Poppy", "Grass, Dandelion", "Bamboo", "Flower: Wild", "Carnivorous Plant"] Vegi_2_Fruit_Vegi = ["Asparagus", "Pinecone", "Berry, Grapes", "Ginger", "Tree fruit (apple, orange)", "Bean", "Pumpkin, Gourd", "Broccoli, Artichoke", "Corn", "Grain, Wheat", "Pineapple"] Vegi_3_Fungi = ["Moss", "Slime Fungi: Ooze, Jelly", "Lichen", "Mushroom"] Vegi_4_Tree = ["Willow", "Birch", "Maple, Oak", "Banyan", "Pine", "Palm"] # Elemental & Mineral Elem_1_Fire_Electric = ["Fire, Vapor", "Electric Bolt", "Ember, Hot Coal", "Molten Lava"] Elem_2_Liquid = ["Icicles", "Fog, Vapor", "Wave", "Dew Drops", "Ripple", "Frost, Snow", "Suds, Bubbles", "Tar, Gum"] Elem_3_Earth_Metal = ["Malachite", "Mountain, Cliff Face", "Brick, Cobblestone", "Rust, Oxide", "Cracked Clay", "Stalactite, Stalagmite", "Glass, Crystals", "Powder, Sand", "Slate, Shale", "Cement, Sediment", "Mercury, Chrome"] Elem_4_Astral_Atmosphere = ["Moon Cycles", "Starfield", "Crater, Asteroid", "Solar Flare", "Galaxy form", "Volcano", "Planets, Saturn's Rings", "Cloud, Cyclone, Turbulence"] #Technical Tech_1_Transportation = ["Car, Truck, Bus", "Aircraft", "Rail, Train, Trolley", "Cycle (motor or bi)", "Sled, Ski", "Boat, Ship", "Spacecraft", "Tank, Caterpillar Tread"] Tech_2_Architecture = [["Ornament, Gargoyle", "Ornament, Pillar"], "Bridge, Framework", "Castle, Dome", "Modern Skyscraper", ["Place of Worship", "Totem", "Cathedral", "Temple"], ["Doorway, Archway", "Window"], "Old Village, Cottage", "Tent"] Tech_3_Tool = ["Drill", ["Cup", "Bowl", "Plate", "Silverware"], "Umbrella", "Bundle, Bale", "Hammer, Axe", "Brush: Hair, Tooth", "Razor, Knife", "Spigot, Faucet", "Rope", "Lock, Key", "Adhesive, Bandage", "Shovel, Pick", "Capsule, Tablet", "Nuts, Bolts", "Chain", "Thread, Stitch", "Shears, Scissors", "Pen, Paintbrush", "Spring, Coil", "Syringe", "Tube, Plumbing", "Wrench, Pliers"] Tech_4_Machine = [["Switch", "Dial, Knob", "Button", "Lever", "Foot Pedal"], ["Bulb, Lamp", "Arc lamp", "Spotlight"], ["Clock, Gears", "Piston, Cylinder"], ["Mechanical Saw","Laser Beam"], ["Reactor Core", "Engine", "Solar Panel"], "Telephone", ["Microchip", "Circuit Board", "Network Room, Cables"], "Dish Antenna", ["Rocket", "Turbine", "Fan, Propeller"]] #Character Elements Emotions = ["Embarrassed", "Anger", "Timid, Bashfull", "Giggle, Smiling", "Squint, Wink", "Bored", "Stressed, Fatigued", "Fear", "Thought, Meditation", "Deadpan", "Insane, Berserker", "Insane, Happy", "Pining, Furrowed", "Laughing, Hysterical", "Attentive, Shock", "Stern, Grumpy", "Clenched Teeth", "Gape, Gawk", "Relief", "Sneering", "Paranoid, Shifty", "Bliss, Joy", "Confusion"] Actions = ["Recoil, Akimbo", "Drenched, Thirst", "Blown by Cyclone", "Push, Pull", "Snoop, Listen", "Crouched for Attack", "Hang, Climb", "Recoil, Head/Torso", "Float, Levitate", "Swinging Weapon", "Twisting, Stretching", "Kicking, Punching", "Squeeze, Tackle", "Absorb, Eat", "Limp, Injured", "Curse, Swear", "Run, Jump", "Melt, Glow, Fire", "Stuck, Trapped", "Pull, Push", "Shoot Weapon", "Dying, Gaunt", "Fly, Swim", "Shed, Molting", "Chant, Recite", "Crawl, Emerge"] # Compliations of the different Sets Anima = [Anim_1_Sea_Life, Anim_2_Insect, Anim_3_Mammal, Anim_4_Reptile, Anim_5_Bird] EleMineral = [Elem_1_Fire_Electric, Elem_2_Liquid, Elem_3_Earth_Metal, Elem_4_Astral_Atmosphere] Veggie = [Vegi_1_Plant, Vegi_2_Fruit_Vegi, Vegi_3_Fungi, Vegi_4_Tree] Techne = [Tech_1_Transportation, Tech_2_Architecture, Tech_3_Tool, Tech_4_Machine] Visual_Elements = [Anima, Veggie, EleMineral, Techne] Character_Elements = [Emotions, Actions] All_Things = [Visual_Elements, Character_Elements] from random import choice def RecurseChoice(Root): # Randomly select from nested lists until you get to # a string instead of a list. Return the string. if isinstance(Root,str): return Root else: Sub_Root = choice(Root) return RecurseChoice(Sub_Root) #Choose one from each category print("Anima: ", end='') print(RecurseChoice(Anima)) print("Veggie: ", end='') print(RecurseChoice(Veggie)) print("EleMineral: ", end='') print(RecurseChoice(EleMineral)) print("Techne: ", end='') print(RecurseChoice(Techne)) print("Emotions: ", end='') print(RecurseChoice(Emotions)) print("Actions: ", end='') print(RecurseChoice(Actions)) # Some test stuff # Run it a ton of times looking for an entry # or just checking for a crash, that too. ''' Search_target = "Narwhale" Number_of_tries = 4000 for i in range(Number_of_tries): result = RecurseChoice(All_Things) if result == Search_target: print("Target found! Try #{}".format(i)) '''
I’ve uploaded a bunch of my python files here: http://peripheralarbor.com/Python/
They are also on GitHub: https://github.com/dudecon/python