Python classes implemetation difficulties - python

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.

Related

How to add aliases to an input dictionary?

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

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!

How would I separate my Python File to multiple plugins?

So first thing I want to say: I have been looking into modules and such, I just don't quiet know how I would rewrite it to fit this in.
Project: What I have is a Skype Bot using the Skype4Py module. I have about 11 commands I noticed the one script is getting a little large.
I'm trying to think about how to link one main.py file to a Plugin Folder, which contains each and every bot function in it's own respectable Python file. It sounds simple an all, except when it comes to how the function is called.
Here is just a basic look at my Skype bot, missing some of the larger functions.
import Skype4Py, random
class SkypeBot():
def __init__(self):
self.skype = Skype4Py.Skype()
if self.skype.Client.IsRunning == False:
self.skype.Client.Start()
self.skype.Attach()
self.results = ['Yes', 'No', 'Maybe', 'Never']
def main(self):
print ' Skype Bot currently running on user: %s' % self.skype.CurrentUserHandle
print "\n\nCommands Called:\n"
while True:
self.skype.OnMessageStatus = self.RunFunction
def RunFunction(self, Message, Status):
if Status == 'SENT' or Status == 'RECEIVED':
cmd = Message.Body.split(' ')[0]
if cmd in self.functions.keys():
self.context = Message
self.caller = self.context.FromHandle
self.functions[cmd](self)
def ping(self):
print " %s : Ping" % self.caller
self.context.Chat.SendMessage('Pong')
def say(self):
try:
response = self.context.Body.split(' ', 1)
if response[1] == "-info":
print " %s : say -info" % self.caller
self.context.Chat.SendMessage("Resends the message entered. \n"
"Usage: !say Hello. \n"
"Example: Bot: Hello.")
else:
say = response[1]
print " %s : Say [%s]" % (self.caller, say)
self.context.Chat.SendMessage(say)
except:
self.context.Chat.SendMessage("Please use -info to properly use the !say command")
def eightball(self):
try:
question = self.context.Body.split(' ', 1)
if question[1] == "-info":
print " %s : 8Ball -info" % self.caller
self.context.Chat.SendMessage("Responds with an answer.\n"
"Usage: !8ball 'Do I have swag?'\n"
"Example: !8Ball Response: 'Yes'")
else:
random.shuffle(self.results)
answer = self.results[3]
print " %s : 8Ball [%s]" % (self.caller, question[1])
self.context.Chat.SendMessage("!8Ball Response: %s" % answer)
except:
self.context.Chat.SendMessage("Please use -info to properly use the !8ball command")
#FUNCTIONS LIST
#********************
functions = {
"!ping": ping,
"!say": say,
"!8ball": eightball,
}
if __name__ == "__main__":
snayer = SkypeBot()
snayer.main()
So basically, what I am wondering, how can I change
self.skype.OnMessageStatus = self.RunFunction
so that it'll run functions from another file?
For a program of this size it's not really necessary to put your command functions into separate files, but I guess it is good organization. And good practice for when you write a program that has thousands of lines of code. :)
One way to do this is to create a basic SkypeBot class without any command methods and then import the command methods from your plugins directory and add them to the class. It's easy enough to add new attributes to an existing class, and it doesn't matter if the new attributes are properties or methods, the syntax to add them is identical. (With a tiny bit more work it's even possible to add new attributes to an instance, so you can have multiple instances, each with their own individual set of commands. But I guess that's not necessary here, since a program that uses the SkypeBot class will normally only create a single instance).
So we can break your question into two parts:
How to add methods to an existing class.
How to import those methods
from other source files.
As I said, 1) is easy. 2) is quite easy as well, but I've never done it before, so I had to do a little bit of research and testing, and I can't promise that what I've done is best practice, but it works. :)
I don't know much about Skype, and I don't have that Skype4Py module, and as you said, the code above is not the complete program, so I've written some fairly simple code to illustrate the process of adding plugin methods from separate files to an existing class.
The name of the main program is "plugin_demo.py". To keep things neat, it lives in its own directory, "plugintest/", which you should create somewhere in your Python path (eg where you normally keep your Python programs). This path must be specified in your PYTHONPATH environment variable.
"plugintest/" has the following structure:
plugintest/
__init__.py
plugin_demo.py
plugins/
__init__.py
add.py
multiply.py
The __init__.py files are used by Python's import machinery to let it know that a directory contains a Python package, see 6.4. Packages in the Python docs for further details.
Here are the contents of those files. Firstly, the files that go into "plugintest/" itself:
__init__.py
__all__ = ['plugin_demo', 'plugins']
from plugintest import *
plugin_demo.py
#! /usr/bin/env python
#A simple class that will get methods added later from plugins directory
class Test(object):
def __init__(self, data):
self.data = data
def add_plugins(cls):
import plugins
print "Adding plugin methods to %s class" % cls.__name__
for name in plugins.__all__:
print name
plug = getattr(plugins, name)
print plug
method = getattr(plug, name)
print method
setattr(cls, name, method)
print
print "Done\n"
add_plugins(Test)
def main():
#Now test it!
t = Test([1, 2, 3]); print t.data
t.multiply(10); print t.data
t.add(5); print t.data
if __name__ == '__main__':
main()
And now the contents of the "plugintest/plugins/" directory:
__init__.py
__all__ = ['add', 'multiply']
from plugintest.plugins import *
add.py
#A method for the Test class of plugin_demo.py
def add(self, m):
self.data = [m + i for i in self.data]
multiply.py
#A method for the Test class of plugin_demo.py
def multiply(self, m):
self.data = [m * i for i in self.data]
If you cd to the directory containing the "plugintest/" folder, you should be able to run it with
python plugintest/plugin_demo.py
and if you cd to "plugintest/" itself
python plugin_demo.py
Also, in the interpreter (or another Python program), you should be able to do
import plugintest
and then run the main() function of "plugin_demo.py" with
plugintest.plugin_demo.main()
The other usual variations of from ... import ... etc should also work as expected.
The function in "plugin_demo.py" that performs the magic of adding the imported methods to the Test class is add_plugins(). When it runs it prints out each method name, its module, and its function. This could be handy during development, but you'd probably comment out some of those print statements once the program's working properly.
I hope this helps, and if you have any questions please don't hesitate to ask.

Python Precommit hook, Looking at lines for some text

Hello, my question is regarding a Python script I am trying to get to work. The point of this is that when someone makes a SVN Commit they see a login template with four lines: Branch, Bug_Number, Feature affected and Overview. Now I am trying to write a script to make sure that they wrote something on it to make sure no one enters a empty log to commit.
Here is what I have so far in python its based on a old python script.
print "Importing the items"
import re
import sys
import os
print "Initializing the list."
argsList = []
hndFile = open(sys.argv[1],"r")
for line in hndFile:
argsList.append(line)
hndFile.close()
print "Checking what is blank"
faOK = ovOK = False
for line in argsList:
line = line.strip()
if line.startswith('FEATURE_AFFECTED:'):
faOK = line[17:] != ''
if line.startswith('OVERVIEW:'):
ovOK = line[9:] != ''
if not faOK:
print "You Must Enter the Feature Affected"
ret = -1
elif not ovOK:
print "You Must Enter an Overview of the Fix"
ret = -1
else:
ret = 0
print "Finishing the script"
sys.exit(ret)
Any advice would help. I am using Windows XP and currently nothing is happening. I am also using collabnet svn. Currently nothing is happening when I try to run this script. I know I haven't added svnlook in the script I cant really think of where to add and for the variable for it. Thank you.

Categories