Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
350 views
in Technique[技术] by (71.8m points)

function - 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 "

Commands Called:
"

        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. 
"
                                              "Usage: !say Hello. 
"
                                              "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.
"
                                              "Usage: !8ball 'Do I have swag?'
"
                                              "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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:

  1. How to add methods to an existing class.
  2. 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
"

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...