Sopel

The Python IRC Bot

Sopel tutorial, Part 4: The .help command

NOTE: This guide is for Sopel 6.0+. If you are still using a version named “Willie”, we strongly encourage you to upgrade, as such old versions are no longer supported.

Sopel has a built in .help command that can be used to get information about a particular command. You can add information to .help about your command in two basic ways. The first is to add a docstring and the second is to add an example usage with the @example decorator. Take the following plugin for example:

@sopel.module.commands('hello')
def helloworld(bot, trigger):
    bot.say('Hello, %s!' % (trigger.group(2),))

It has no docstring or example so saying .help hello will do nothing. If the program is modified to include either of them like so:

@sopel.module.commands('hello')
@sopel.module.example('.hello world', 'Hello, world!')
def helloworld(bot, trigger):
    """Replies with what ever the first parameter is."""
    bot.say('Hello, %s!' % (trigger.group(2),))

Then we might get the following output:

User> .help hello
Sopel> User: Replies with what ever the first parameter is.
Sopel> e.g. .hello world

Unit tests with the @example decorator

The second parameter of @example is optional and provides a way of documenting what the expected output is. If you do add the expected output however, Sopel will automatically make unit-tests to check that the example output is what really results from calling the command with the example input.

To run the unit tests and see the results you must have pytest installed and run pytest_run.py in Sopel’s directory. You should see something like the following:

C:\Users\Venti\workspace2\sopel>python pytest_run.py
============================= test session starts =============================
platform win32 -- Python 2.7.5 -- pytest-2.3.5
collected 27 items / 12 errors

sopel/modules/calc.py .FF.
sopel/modules/dice.py .......
sopel/modules/exampletest.py .
sopel/modules/rand.py .....
sopel/modules/units.py .........
sopel/modules/url.py .
=================================== ERRORS ====================================
(stuff removed for brevity)
================ 2 failed, 25 passed, 12 error in 4.63 seconds ================

You can also run the unit tests of your plugin directly by appending the following to the end of your plugin:

if __name__ == "__main__":
    from sopel.test_tools import run_example_tests
    run_example_tests(__file__)

Now if you run the the plugin directly in your IDE or in command line, you should get something like the following:

C:\Users\Venti\workspace2\sopel>set PYTHONPATH=C:\Users\Venti\workspace2\sopel
C:\Users\Venti\workspace2\sopel>python sopel\modules\exampletest.py
============================= test session starts ==============================
platform win32 -- Python 2.7.5 -- pytest-2.3.5
collected 1 items

exampletest.py .

=========================== 1 passed in 0.01 seconds ===========================

Although only the first example will be added to the .help command, as many @example decorators as needed can be added for unit tests. This can be quite useful when making changes or adding new features.

We could for example add a test for the edge condition of calling .hello with no arguments:

@sopel.module.example('.hello', 'Hello, !')

And then we can rerun the tests without restarting Sopel or even reloading the plugin, instantly seeing the difference between expectation and reality:

============================= test session starts ==============================
platform win32 -- Python 2.7.5 -- pytest-2.3.5
collected 2 items

exampletest.py F.

=================================== FAILURES ===================================
__________________________ test_example_helloworld_0 ___________________________
Traceback (most recent call last):
  File "C:\Users\Venti\workspace2\sopel\sopel\test_tools.py", line 112, in test
    assert result == output
AssertionError: assert 'Hello, !' == 'Hello, None!'
  - Hello, !
  + Hello, None!
  ?        ++++
====================== 1 failed, 1 passed in 0.03 seconds ======================

Using regular expressions with the @example decorator

TODO

Want to distribute your plugin as a package on PyPI? Continue to part 5!