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!')