Extension Development

Foreword

Currently extensions are tied very closely to python’s packaging ecosystem. This is unlikely to change, honestly.

Setup a Packging Environment

$ # Note that the directory these commands are run from is `~/my_vexbot_extensions`!

$ mkdir my_vexbot_extenstions
$ cd my_vexbot_extensions

Create a setup.py file.

# filepath: my_vexbot_extensions/setup.py
from setuptools import setup

setup(name='my_vexbot_extensions')

Now take a breather. Python packaging is hard, and we’re almost done!

Extensions Development

Done with your breather? Good. Now let’s say we’ve got a hello_world function that we want to use such as the below.

# filepath: my_vexbot_extensions/hello_world.py

def hello(*args, **kwargs):
    print('Hello World!')

Dope. Let’s add this as an extension in our setup.py file.

# filepath: my_vexbot_extensions/setup.py
from setuptools import setup

setup(name='my_vexbot_extensions',
      entry_points={'vexbot_extensions':['hello=hello_world:hello'])

That’s it. Let’s make sure the package is installed on our path. Make sure you’re in the directory my_vexbot_extensions (or whatever you named your folder).

$ # Note that the directory these commands are run from is `~/my_vexbot_extensions`!

$ ls
setup.py    hello_world.py

$ python setup.py develop

You might say, wait, that’s not a normal python packaging structure! And you’d be right, so let’s look at how it’ll change for that. We’ll make a directory called my_src and create a file in there known as goodbye_world.py

# filepath: my_vexbot_extensions/my_src/goodbye_world.py

def angsty(*args, **kwargs):
    print('Goodbye, cruel world!')

Note how I’m taking an arbitrary amount of arguments and key word arguments using *args and **kwargs? You should do that for every extension, or your program will error out at somepoint when it gets called with metadata that it’s not ready for.

# filepath: my_vexbot_extensions/my_src/goodbye_world.py

def angsty(*args, **kwargs):
    print('Goodbye, cruel world!')

# Note: Do NOT make extensions without using `*args, **kwargs`
def function_that_will_fail_on_metadata():
    print('Please notice the lack of flexibility in this function for taking arguments')
    print('This type of extension will inevitabley throw `TypeError` exceptions if put in a codebase')

But back to registering our extension in our setup.py file. Remember that the filepath for this is my_vexbot_extensions/my_src/goodbye_world.py.

# filepath: my_vexbot_extensions/setup.py
from setuptools import setup

setup(name='my_vexbot_extensions',
      entry_points={'vexbot_extensions':['hello=hello_world:hello',
                                         'angsty=my_src.goodbye_world:angsty'])

Notice how we use the . operator to represent the folder directory, and the : to specify the method name? That’s important.

We can have multiple methods in a file that way.

# filepath: my_vexbot_extensions/my_src/goodbye_world.py

def angsty(*args, **kwargs):
    print('Goodbye, cruel world!')

def crocodile(*args, **kwargs):
    print('Goodbye, crocodile!')
# filepath: my_vexbot_extensions/setup.py
from setuptools import setup

setup(name='my_vexbot_extensions',
      entry_points={'vexbot_extensions':['hello=hello_world:hello',
                                         'angsty=my_src.goodbye_world:angsty',
                                         'crocodile=my_src.goodbye_world:crocodile'])

If you have a deeply python nested file, such as one in my_vexbot_extensions/my_src/how/deep/we/go.py… .. code-block:: python

# filepath: my_vexbot_extensions/setup.py from setuptools import setup

setup(name=’my_vexbot_extensions’,
entry_points={‘vexbot_extensions’:[‘hello=hello_world:hello’,
‘angsty=my_src.goodbye_world:angsty’, ‘crocodile=my_src.goodbye_world:crocodile’, ‘nested_func=my_src.how.deep.we.go:waay_to_much’])

Note that each folder is separated by the . operator, and the function name in the above example is waay_to_much, which is how deeply I feel that function is nested for a simple example such as this.

Remember, remember the 5th of November. And also to re-run python setup.py develop once you’ve added an entry point/extension to your setup.py file.

$ # Note that the directory these commands are run from is `~/my_vexbot_extensions`!

$ ls
setup.py    hello_world.py    my_src

$ python setup.py develop

The string used before the path decleration, I.e the nested_func in the string nested_func=my_src.how.deep.we.go:waay_to_much is the name you will use in vexbot itself or the hello in hello=hellow_world:hello.

Let’s add our hello world greeting to our command line interface.

$ vexbot

vexbot: !add_extensions hello
vexbot: !hello
Hello World!

You can also add the hello world to the robot instance as well.

$ vexbot

vexbot: !add_extensions hello --remote

Last, but not least, you can specify command name alias’s, specify if the command should be hidden, and give a short description for what the command does by using the command decorator.

# filepath: my_vexbot_extensions/hello_world.py
from vexbot.commands import command


@command(alias=['hello_world', 'world_hello'],
         hidden=True, # default is `False`
         short='Prints `Hello World`!')
def hello(*args, **kwargs):
    print('Hello World!')