Dynamically create functions from Json - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Dynamically create functions from Json (/thread-16275.html) |
Dynamically create functions from Json - Clement_2000 - Feb-20-2019 Bonjour experts ! It is still for my printer controller project. I'm now trying to make a lib to get a flexible, and easy tom modify command list. So the goal for now is that I want to create a custom user command list, with functions declared in a class. So here is what I've done : #File name = test.py from printer import * ML390 = Printer(1, "commands.json") ML390.formFeed() #File name = commands.json [ {"name":"lineFeed", "command":"x0A"}, {"name":"carriageReturn", "command":"x0D"}, {"name":"formFeed", "command":"x0C"}, {"name":"setLetterQuality", "command":"x1B x78 x32"}, {"name":"setUtility", "command":"x1B x78 x31"} ] #File name = printer.py import platform #Lib to check os import json class Printer(): #Main class def __init__(self, port, file): #"port" is the parralel port where the printer is pluged in, "file" is the json file containg the custom functions and command to send to the printer self.os = platform.system() #Returning os in a variable self.data = json.load(open(file)) #Opening user selected file if type(port) != int: #Checks if the port is an int raise Exception("PORT ERROR : Port must be an intreger") #Returns error if port <= 0: #Checks if the port is the right one raise Exception("PORT ERROR : Port must be 1 or higher") #returns error if self.os == "Linux" : #If on Linux self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx elif self.os == "Windows" : #If on Winows self.port = open("LPT"+str(port), "w") #Open as LPTx else: #If on oter system raise Exception("OS ERROR : Unsupported OS") #No supported os error for n in range(len(self.data)): #Cor all the commands defined in the json exec(f"""def {self.data[n]["name"]}(self): self.printText(data[n]["command"])""") #Create a custom function from the json, function name is name in the json def printText(self, text): text = str(text) self.port.write(text) self.port.flush()When running test.py, I get this error : I don't know how to declare the functions in the main class, but keep the dynamic function creation when calling the class (__init__). . As I would say in my country : "Je suis chiffoné !" Help me please. Thank you in advance, Clément. RE: Dynamically create functions from Json - nilamo - Feb-20-2019 You're defining functions that are local to the __init__ method, so they won't be accessible to the class as a whole. That can be fixed, but I'm not sure it should be. Dynamically generating code is pretty sketchy, and you should be very suspicious whenever you see the words "exec" or "eval" in source. For starters, how are you planning on calling these dynamic functions? What if one of the functions is "printText"? Would all the others stop working, because they're trying to use a version of printText which no longer exists? I think it'd make more sense for you to load them as a dict, then have a single method that looks them up. So instead of ML390.formFeed() , you'd do ML390.dispatch("formFeed") . Then that function would look something like: def dispatch(self, key): if key in self.data: return self.printText(self.data[key]) raise Exception(f"Invalid key: {key}") RE: Dynamically create functions from Json - Larz60+ - Feb-20-2019 the best way to create a command driven execution is to use a dictionary, here's a simple example: class SoftwareCommands: def __init__(self): self.commands = { 'func_a': self.func_a, 'func_b': self.func_b, 'willy nilly': self.willy_nilly } def func_a(self): print("\nHi, I'm func_a") def func_b(self): print("\nHi, I'm func_b") def willy_nilly(self): n = 0 while n < 10: print('n: {}'.format(n)) n += 1 def main(): command = None sc = SoftwareCommands() while command != 'quit': command = input("Enter a command, quit to end: ").strip().lower() if command in sc.commands: sc.commands[command]() if __name__ == '__main__': main()outout:
RE: Dynamically create functions from Json - Clement_2000 - Feb-21-2019 Hi nilamo and Larz60+, Thank you for your help ! So nilmao, you're totally right. I don't know why I haven't done that before, I'm stupid. So i worked on it. Larz60+, I saw your code, that's not exactly what I was looking for, but nilmao found what I was looking for. Anyway, I keep it in mind, it could be useful to another project. Anyway, following your idea nilmao, here is what I ended up with : #NAME = test.py from printer import * ML390 = Printer(1, "commands.json") ML390.command("setUtility") #NAME = commands.py { "lineFeed":{"command":"0A", "text":"Line Feed"}, "carriageReturn":{"command":"0D", "text":"Carriage Return"}, "formFeed":{"command":"0C", "text":"formFeed"}, "setLetterQuality":{"command":"1B 78 32", "text":"Set Letter Quality"}, "setUtility":{"command":"1B 78 31", "text":"Set Utility"} } #NAME = printer.py import platform #Lib to check os import json class Printer(): #Main class def __init__(self, port, file): #"port" is the parallel port where the printer is plugged in, "file" is the json file containing the custom functions and command to send to the printer self.os = platform.system() #Returning os in a variable self.data = json.load(open(file)) #Opening user selected file if type(port) != int or port <= 0: #Checks if the port is an int raise Exception(f"Invalid port : {port}") #Returns error if self.os == "Linux" : #If on Linux self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx elif self.os == "Windows" : #If on Winows self.port = open("LPT"+str(port), "w") #Open as LPTx else: #If on oter system raise Exception(f"Invalid OS : {self.os}") #No supported os error def printText(self, text): self.port.write(str(text)) self.port.flush() def command(self, key): #custom command generator if key in self.data: #If the key is in the json commands = self.data[key]["command"] #Extract the command string from the json print(commands) #TEMPORARY commands = commands.split(" ") #Split each hex number individually for n in range(len(commands)): #For each hex number commands[n] = f"\x{commands[n]}" #add "\x" before commands = "".join(commands) #Put everything back into a single string self.printText(commands) #Send the command to the printer print(self.data[key]["text"]) #TEMPORARY print(commands) #TEMPORARY else: #If the key isn't in the json raise Exception(f"Invalid key: {key}") #Wrong key errorThe new problem now is that in line 31, (you already know what will happen ), python tells me that I have to put the hex value after. And if I replace the string by "\\x" or r"\x", when running the command, I the printer litearally prints "\x0A" or "\x0B", or ... (not the quotes). Should I create an other thread or can you help me now ? Thanks again (I never say it enough ), Clément. RE: Dynamically create functions from Json - nilamo - Feb-21-2019 Does this help? >>> code = "1B" >>> as_int = int(code, base=16) >>> as_int 27 >>> as_hex = hex(as_int) >>> as_hex '0x1b' RE: Dynamically create functions from Json - Clement_2000 - Feb-22-2019 Hi nilamo, I'm so happy today, you can't believe. Everything is fixed ! Thank you ! When reading your message, this did remind me a previous message of Larz60+ in this thread https://python-forum.io/Thread-Control-a-dot-matrix-printer. And was showing the use of chr(). So I converted all my hex values into decimal values in the json. And I changed a bit the lib to use chr()... And that's all ! Here is the result #NAME = commands.json { "lineFeed":{"command":"10", "text":"Line Feed"}, "carriageReturn":{"command":"13", "text":"Carriage Return"}, "formFeed":{"command":"12", "text":"Form Feed"}, "newLine":{"command":"10 13", "text":"New Line"}, "setLetterQuality":{"command":"27 120 49", "text":"Set Letter Quality"}, "setUtility":{"command":"27 120 48", "text":"Set Utility"}, "fontRoman":{"command":"27 107 48"}, "fontHelvette":{"command":"27 107 49"}, "fontCourrier":{"command":"27 107 50"}, "fontPrestige":{"command":"27 107 51"}, "fontGothic":{"command":"27 107 54"}, "fontBold":{"command":"27 107 55"}, "styleNormal":{"command":"27 113 0"}, "styleOutline":{"command":"27 113 1"}, "styleShadow":{"command":"27 113 2"}, "styleOutlineShadow":{"command":"27 113 3"}, "10Cpi":{"command":"27 80 18"}, "12Cpi":{"command":"27 77 18"}, "15Cpi":{"command":"27 103 18"}, "17Cpi":{"command":"27 80 15"}, "20Cpi":{"command":"27 77 15"}, "propOn":{"command":"27 112 1"}, "propOff":{"command":"27 112 0"}, "doubleOn":{"command":"27 119 49 27 87 49"}, "doubleOff":{"command":"27 119 48 27 87 48"} } #NAME = printer.py import platform #Lib to check os import json class Printer(): #Main class def __init__(self, port, file): #"port" is the parralel port where the printer is pluged in, "file" is the json file containg the custom functions and command to send to the printer self.os = platform.system() #Returning os in a variable self.data = json.load(open(file)) #Opening user selected file if type(port) != int or port <= 0: #Checks if the port is an int raise Exception(f"Invalid port : {port}") #Returns error if self.os == "Linux" : #If on Linux self.port = open("/dev/lp"+str(port-1), "w") #Open as /dev/lpx elif self.os == "Windows" : #If on Winows self.port = open("LPT"+str(port), "w") #Open as LPTx else: #If on oter system raise Exception(f"Invalid OS : {self.os}") #No supported os error def printText(self, text): self.port.write(str(text)) self.port.flush() def command(self, key): #custom command generator if key in self.data: #If the key is in the json commands = self.data[key]["command"] #Extract the command string from the json commands = commands.split(" ") #Split each dec number individually for n in range(len(commands)): #For each dec number self.printText(chr(int(commands[n]))) #Send the command to the printer else: #If the key isn't in the json raise Exception(f"Invalid key: {key}") #Wrong key error def sendAscii(decimals): decimals = decimals.split(" ") for n in range(len(decimals)): printText(chr(decimals[n])) #NAME = test.py from printer import * ML390 = Printer(1, "commands.json") ML390.command("cleanPaperBin") ML390.command("setLetterQuality") ML390.command("10Cpi") ML390.command("doubleOn") ML390.command("underlineOn") ML390.command("boldOn") ML390.command("styleOutlineShadow") ML390.command("fontGothic") ML390.command("alignCenter") ML390.command("newLine") ML390.printText("****************************************\n") ML390.printText("Thank you nilamo and Larz60+ !\n\n") ML390.printText("You solved all my problems...\n\n") ML390.command("newLine") ML390.command("newLine") ML390.command("alignLeft") ML390.command("20Cpi") ML390.printText("...at least for now ;-p...\n\n") ML390.command("10Cpi") ML390.command("alignCenter") ML390.printText("****************************************\n\n") ML390.command("formFeed") ML390.command("reset")[Image: printer1.jpg] [Image: printer2.jpg] I'm so glad it works ! Now the GUI is left to do ! Thanks again, Clément. |