How would I separate my Python File to multiple plugins?
Question: 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?


Answer: 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](https://docs.python.org/2/tutorial/modules.html#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.

