How to add aliases to an input dictionary? - python

Recently I started a project. My goal was it to have a script, which, once launched, could be able to control actions on the hosts computer if an instruction was send via email. (I wanted this so I could start tasks which take a long time to complete while I'm away from home)
I started programming and not long after I could send emails, receive emails and analyze their content and take actions responding to the content in the email.
I did this by using an input dictionary, which looked like this:
contents_of_the_email = "!screen\n!wait 5\n!hotkey alt tab"
def wait(sec):
print(f"I did nothing for {sec} seconds!")
def no_operation():
print("Nothing")
def screenshot():
print("I took an image of the screen and send it to your email adress!")
def hotkey(*args):
print(f"I pressed the keys {', '.join(args)} at the same time")
FUNCTIONS = {
'':no_operation,
'!screen': screenshot,
'!hotkey': hotkey,
'!wait': wait
}
def call_command(command):
function, *args = command.split(' ')
FUNCTIONS[function](*args)
for line in contents_of_the_email.split("\n"):
call_command(line)
In total I have around 25 functions each with its own response. I replaced the actual code for the commands with simple print statements as they are not needed to understand or replicate my problem.
I then wanted to add aliases for the command, so for example you would be able to type "!ss" instead of "!screen".
I did achieve this using another line in the dictionary:
FUNCTIONS = {
'':no_operation,
'!screen': screenshot,
'!ss':screenshot,
'!hotkey': hotkey,
'!wait': wait
}
But I didn't like this. It would fill up the whole dictionary if I did it for every alias I am planning to add and it would make my code very messy.
Is there any way to define aliases for commands separately and still keep the dictionary clean and simple? I would desire something like this in a separate aliases.txt file:
screen: "!screen", "!ss","!screenshot","!image"
wait: "!wait","!pause","!sleep","!w"
hotkey: "!hk","!tk"
If this is possible in python I would really appreciate to know!

You can use following solution:
import json
contents_of_the_email = "!screen\n!wait 5\n!hotkey alt tab"
def wait(sec):
print(f"I did nothing for {sec} seconds!")
def no_operation():
print("Nothing")
def screenshot():
print("I took an image of the screen and send it to your email address!")
def hotkey(*args):
print(f"I pressed the keys {', '.join(args)} at the same time")
# FUNCTIONS DICT FROM JSON
with open("aliases.json") as json_file:
aliases_json = json.load(json_file)
FUNCTIONS = {}
for func_name, aliases in aliases_json.items():
FUNCTIONS.update({alias: globals()[func_name] for alias in aliases})
def call_command(command):
function, *args = command.split(' ')
FUNCTIONS[function](*args)
for line in contents_of_the_email.split("\n"):
call_command(line)
aliases.json:
{
"screenshot": ["!screen", "!ss","!screenshot","!image"],
"wait": ["!wait","!pause","!sleep","!w"],
"hotkey": ["!hk","!tk", "!hotkey"]
}
is that what you looking for?

You can go from a dictionnary of callables and list of shortcuts to a dictionnary of shortcuts to callables fairly easily with for loops.
# long dict of shortcuts to callables
goal = {'A': 0, 'B': 0, 'C': 1}
# condensed dict, not in .txt, but storable in python
condensed = {0: ['A', 'B'], 1: ['C']}
# expand the condensed dict
commands = {}
for func, shortcuts in condensed.items():
for shortcut in shortcuts:
commands[shortcut] = func
# or with a comprehension
commands = {s: f for f, ls in condensed.items() for s in ls}
# verify expanded and goal are the same
assert commands == goal

You could do what you want by first creating a dictionary mapping each alias to one of the functions. This would require parsing the aliases.txt file — which fortunately isn't too difficult. It make use of the ast.literal_eval() function to convert the quoted literal strings in the file into Python strings, as well as the built-in globals() function to look up the associated functions given their name it the file. A KeyError will be raised if there are any references to undefined functions.
Note I changed your aliases.txt file to the following (which makes a little more sense):
screenshot: "!screen", "!ss","!screen","!image"
wait: "!wait","!pause","!sleep","!w"
hotkey: "!hk","!tk"
Below is a runnable example of how to do it:
from ast import literal_eval
ALIASES_FILENAME = 'aliases.txt'
# The functions.
def wait(sec):
print(f"I did nothing for {sec} seconds!")
def no_operation():
print("Nothing")
def screenshot():
print("I took an image of the screen and send it to your email adress!")
def hotkey(*args):
print(f"I pressed the keys {', '.join(args)} at the same time")
# Create dictionary of aliases from text file.
aliases = {}
with open(ALIASES_FILENAME) as file:
namespace = globals()
for line in file:
cmd, other_names = line.rstrip().split(':')
aliases[cmd] = namespace[cmd] # Allows use of actual function name.
for alias in map(literal_eval, other_names.replace(',', ' ').split()):
aliases[alias] = namespace[cmd]
def call_command(command):
function, *args = command.split(' ')
if function in aliases:
aliases[function](*args)
# Sample message.
contents_of_the_email = """\
!screen
!wait 5
!hk alt tab
"""
# Execute commands in email.
for line in contents_of_the_email.split("\n"):
call_command(line)
Output:
I took an image of the screen and send it to your email adress!
I did nothing for 5 seconds!
I pressed the keys alt, tab at the same time

Related

Python classes implemetation difficulties

Every time that I try to run this code in the python idle it says that my indentation is incorrect, and I have done numerous alterations to try to get it running, none of which worked. Any advice you could give me to get it working?
from random import randint
class enemy:
def __init__(self, type, bhealth, dmglvl):
self.type = type
self.bhealth = int(bhealth)
self.dmglvl = int(dmglvl)
enhealth=self.bhealth
def eninfo(self):
return '{} {} {}'.format(self.type, self.bhealth, self.dmglvl)
def battleintro(self):
return 'an enemy {} has appeared! \nIt has: {} health.'.format(self.type, self.bhealth)
def hit(self):
print('You swing your sword at the {}.'.format(self.type))
chardmg=randint(8,15)
enhealth-=chardmg
print('You deal {} damage. The {} has {} hit-points left.'.format(chardmg, self.type, enhealth))
if enhealth <=1:
return 'The {} Died!'.format(self.type)
enemy1 = enemy("goblin", 15, 1)
print(enemy1.eninfo())
print(enemy1.battleintro())
Are you typing this as a file, or are yu trying to type this directly into the interactive interpreter - the default screen when idle opens up?
The interactive intrepreter is meant for testing and short code sequences, not for one to try to type entire classes - but overall, it does have some key differences to what you can type in an actual program file: the most proemminent being that at the end of a code block you have to skip a line (i.e. an extra blank line) - otherwise it will give you an error.
So you'd have to include a whiteine before your "enmey1 = ..." line - but rather, you have to type it as a "myfile.py" file, not in the interpreter, and afterwards run it with "import myfile" to test it interactively.

Return a non-repeating, random result from a list inside a function

I am writing a Python program that responds to request and vocalizes a response back to the user. Below is a sample of two functions. How can I do this without using a global variable and still get back a non-repeating, random response?
# stores prior response
website_result = 'first_response.wav'
def launch_website():
# if service is offline return with default msg otherwise launch service
if is_connected() == 'FALSE':
arg = 'this_service_is_offline.wav'
return arg
else:
site = 'http://www.somesite.com'
launch_it(site)
return launch_website_response()
def launch_website_response():
# using the global variable inside function
global website_result
# possible responses
RESPONSES = ['first_response.wav', 'second_response.wav', 'third_response.wav']
# ensures a non-repeating response
tmp = random.choice(RESPONSES)
while website_result == tmp:
tmp = random.choice(RESPONSES)
website_result = tmp
return website_result
Your website_result variable indicates that you to have some sort of persistent state. Maybe you could consider storing it in a text file and access it everytime you need it and change it afterward (this works if you don't have to do too many calls to it, otherwise you will get in I/O limitations).
I don't know about the specifics of your application, but it might happen that you could also make your two functions take website_result as an argument as suggested by #JGut.

What's the best way to implement an SCPI command tree structure as Python class methods?

SCPI commands are strings built of mnemonics that are sent to an instrument to modify/retrieve its settings and read measurements. I'd like to be able to build and send a string like this: "SENSe:VOLTage:DC:RANGe 10 V"
With code like this:
inst.sense.voltage.dc.range('10 V')
But it seems like a very deep rabbit hole and I'm not sure I want to start down it. Loads of classes for each subsystem and option....
Would a better approach be something like:
def sense_volt(self, currenttype='DC', RANG='10 V'):
cmdStr = 'SENS:VOLT:' + currenttype + ':RANG: ' + RANG
return self.inst.write(cmdStr)
It's trivial to just implement the commands I need and leave inst.query(arg) and inst.write(arg) methods for manually building additional commands, but I'd like to eventually have a complete instrument interface where all commands are covered by autocompleteable methods.
I managed it, though I'm pretty unhappy with the results. It's been three years since I touched any of this, so I'm not sure why I did some things the way I did. I know I had a lot of trouble wrapping my head around organizing everything for portability. I'm sure that now, after a long wait, I'll be inundated with suggestions for how I should have done it.
I wrote parsing functions to interpret SCPI commands. They can separate the command from the arguments, determine required and optional arguments, recognize queries, etc. For example:
def command_name(scpi_command):
cmd = scpi_command.split(' ')[0]
new = ''
for i, c in enumerate(cmd):
if c in '[]':
continue
if c.isupper() or c.isdigit():
new += c.lower()
continue
if c == ':':
new += '_'
continue
if c == '?':
new += '_qry'
break
return new
The command from CONFigure[:VOLTage]:DC [{<range>|AUTO|MIN|MAX|DEF} [, {<resolution>|MIN|MAX|DEF}]] can be parsed as conf_volt_dc, or conf_dc. I did not use the abridged option in my experiments. I think some of my dissatisfaction stemmed from the extra-long method names.
I wrote a builder script to read the commands from a file, parse them, and write a new script with a "command set" class extending a "command handler." Every command is parsed and converted into one of a few boilerplate methods, such as:
query_str_no_args = r'''
def {name}(self):
"""SCPI instrument query.
{s}
"""
cmd = '{s}'
return self._command_handler(command=cmd)
'''
The parser also includes a command handler class to process commands and arguments. Each command is parsed from its example text, ala >>> _command_handler(0.1, 'MAX', command='CONFigure[:VOLTage]:DC [{<range>|AUTO|MIN|MAX|DEF} [, {<resolution>|MIN|MAX|DEF}]]'. Arguments (if any) are validated, and the command string is rebuilt and sent to the instrument,
class Cmd_Handler():
def _command_handler(self, *args, command):
# command string 'SENS:VOLT:RANG'
cmd_str = command_string(command)
# argument dictionary {0: [True, '<range>, 'AUTO', 'MIN', 'MAX', 'DEF'],
# 1: [False, '<resolution>', 'MIN', 'MAX', 'DEF']}
arg_dict = command_args(command)
if debug_mode: print(arg_dict)
for k, v in arg_dict.items():
for i, j in enumerate(v[1:]):
...
# Validate arguments
# count mandatory arguments
if len(args) < len([arg_dict[k] for k in arg_dict.keys() if arg_dict[k][0]]):
...
if '?' in cmd_str:
return self._query(cmd_str + arg_str)
else:
return self._write(cmd_str + arg_str)
When the builder is done, you end up with a big script (the Ag34401 and Ag34461 files were 2600 and 3600 lines, respectively) that looks like this:
#!/usr/bin/env python3
from scpi.scpi_parse import Cmd_Handler
class Ag34461A_CS(Cmd_Handler):
...
def calc_scal_stat(self, *args):
"""SCPI instrument command.
CALCulate:SCALe[:STATe] {OFF|ON}
"""
cmd = 'CALCulate:SCALe[:STATe] {OFF|ON}'
return self._command_handler(*args, command=cmd)
def calc_scal_stat_qry(self):
"""SCPI instrument query.
CALCulate:SCALe[:STATe]?
"""
cmd = 'CALCulate:SCALe[:STATe]?'
return self._command_handler(command=cmd)
Then you extend this class with your short, sweet instrument class:
#!/usr/bin/env python3
import pyvisa as visa
from scpi.cmd_sets.ag34401a_cs import Ag34401A_CS # Extend the command set
class Ag34401A(Ag34401A_CS):
def __init__(self, resource_name=None):
rm = visa.ResourceManager()
self._inst = None
if resource_name:
inst = rm.open_resource(resource_name)
else:
for resource in rm.list_resources():
inst = rm.open_resource(resource)
if '34401' in inst.query("*IDN?"):
self._inst = inst
break
if not self._inst:
raise visa.errors.VisaIOError
assert isinstance(self._inst, visa.resources.GPIBInstrument)
self._query = self._inst.query
self._write = self._inst.write
I also have a SCPI instrument parent class that I was trying to fill with all of the star commands (*IDN?, *CLS, etc.) I wasn't happy with the previous results and was intimidated by some of the star commands' mounting complexity (some of which seemed really instrument-specific), and abandoned it. Here's the first bit
class SCPI_Instrument(object, metaclass=ABCMeta):
#abstractmethod
def query(self, message, delay):
pass
#abstractmethod
def write(self, message, delay):
pass
# *CLS - Clear Status
def cls(self, termination = None, encoding = None):
"""Clears the instrument status byte by emptying the error queue and clearing all event registers.
Also cancels any preceding *OPC command or query."""
return self.write("*CLS", termination, encoding)
In the end, the documentation I was able to squeeze into it wasn't as helpful as I wanted. The autocomplete wasn't able to prompt for arguments by name, and having every single command suggested was less helpful than I expected. Trying to keep the import paths straight was difficult enough for me, much less someone who might end up using my code.
Ultimately, coding has never been part of my job description, and I was moved to a position where experimenting with instrument control is even less possible.

Sublime Text 3 Plugin changing run to on_pre_save

I have been working on a Sublime Text 3 plugin that fixes some coding standards I have at work(That I have a bad habit of missing) I currently have this working with a command run in the console. Most of the code was originally from this thread.
import sublime, sublime_plugin
class ReplaceCommand(sublime_plugin.TextCommand):
def run(self, edit):
#for each selected region
region = sublime.Region(0, self.view.size())
#if there is slected text
if not region.empty():
#get the selected region
s = self.view.substr(region)
#perform the replacements
s = s.replace('){', ') {')
s = s.replace('}else', '} else')
s = s.replace('else{', 'else {')
#send the updated string back to the selection
self.view.replace(edit, region, s)
Then you just need to run:
view.run_command('replace')
And it will apply the coding standards(there are more I plan to implement but for now i'll stick with these) I would like this to run on save.
I tried just changing run(self, edit) to on_pre_save(self, edit) but it does not work. I don't get any syntax errors but It just doesn't work.
Can anyone tell me how to make this run on save instead of having to run the command?
On ST3 the only way to get an Edit object is by running a TextCommand. (It's in the docs, but they're not terribly clear). But, fortunately, you can run the command pretty much the same way you have been doing.
Events handlers, like on_pre_save, can only be defined on an EventListener. The on_pre_save() event is passed a view object so you just need to add something like this, which kicks-off the command you've already written.
class ReplaceEventListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
view.run_command('replace')
The Solutions I came to was to create an on_pre_save() function that runs the command I listed earlier:
import sublime, sublime_plugin
class ReplaceCodeStandardsCommand(sublime_plugin.TextCommand):
def run(self, edit):
#for each selected region
region = sublime.Region(0, self.view.size())
#if there is slected text
if not region.empty():
#get the selected region
s = self.view.substr(region)
#perform the replacements
s = s.replace('){', ') {')
s = s.replace('}else', '} else')
s = s.replace('else{', 'else {')
#send the updated string back to the selection
self.view.replace(edit, region, s)
class ReplaceCodeStandardsOnSave(sublime_plugin.EventListener):
# Run ST's 'unexpand_tabs' command when saving a file
def on_pre_save(self, view):
if view.settings().get('update_code_standard_on_save') == 1:
view.window().run_command('replace_code_standards')
Hopefully this code helps someone!

Call a function through a variable in Python

I'm trying to make a game engine in Python as a "weekend project" but I have a problem.
I'm trying to make it that a user can declare a keybind by typing the key and the function into a text file what they want to run but when I run the code from that sheet with exec it runs the function and I have no idea on how to call the function through the variable. (Also I don't want it to run the function when it runs the code.)
Here is the code I use for executing the code from binds.zdata
for line in bind:
try:
exec line
except:
try:
error.write(localtime + " : " + "ERROR: Could not bind key from the bind file : " + line)
except:
pass
Here's the text in binds.zdata
_w = Functions.motion.move().forward()
_a = Functions.motion.move().left()
_s = Functions.motion.move().back()
_d = Functions.motion.move().right()
You want to lose the () at the end of each line:
_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right
now you can call the function through the variable by simply applying parenthesis, such as:
_w()
I'm not sure I recommend "exec" since somebody could put any code in there.
But here is your problem.
_w = Functions.motion.move().forward()
Calls the function 'forward' and puts the results in _w.
_w = Functions.motion.move().forward
Assigns the function 'forward' to the variable '_w'.
Since you asked what I would do, I would create a set of tokens that represent the various functions and then let them do the mapping in side a config file (see: ConfigParser). Then parse the config file. It is a little more work, but a lot more secure.
I'd first change the lines in the binds.zdata file like this so they assign a function to each of the variables:
_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right
Then, assuming that Functions is defined, I'd execute all the lines in the file at once with something like:
try:
execfile('binds.zdata')
except Exception as exc:
error.write(localtime + " : " + "ERROR: executing binds file")
Afterwards, the _w, _a, _s, _d variables will have the desired function assigned to them, which you can call as usual — i.e. _w(), _a(), etc.
Reading the config like that with exec is going to open you to all kinds of pain at runtime, as the user's config file is going to be executable code that you are just trusting. Malicious or unintentionally just bad commands will get execute. You could template it like this:
def read_config(config_file):
user_button_map = {}
with open(config_file, 'r') as fh:
for line in fh.readlines():
key,func_key = line.strip().split(',')
assert(key in BUTTON_MAP_FUNCTIONS),"no such action"
user_button_map[key] = BUTTON_MAP_FUNCTIONS[func_key]
return user_button_map
BUTTON_MAP_FUNCTIONS = {
"forward" : Functions.motion.move().forward,
"left" : Functions.motion.move().left,
# etc...
}
# sample config file
# w, forward
# a, left
# etc...

Categories