Debugging tips


These notes exist to assist developers in debugging their hosts in real time. Do not follow any advice here without being prepared for the consequences. Requests for support will be void on improperly-tampered hosts.

Run with full output

Launch Python in developer mode to get stdlib deprecations and asyncio resource debugging:

$ python3 -X dev -X tracemalloc -m immp config.yml

Configure your logging for the runner to output at DEBUG level:

  version: 1
      (): immp.LocalFilter
      class: logging.StreamHandler
      filters: [local]
      class: logging.FileHandle
      filename: internal.log
      level: DEBUG
    level: DEBUG
    handlers: [console, internal]
  disable_existing_loggers: false

This will print local debug output (from the immp namespace) to stdout, and everything (IMMP hooks/plugs as well as their dependencies) to a log file called internal.log.

Enable the async shell

The Shell is an invaluable tool for poking internals, so make sure to have it enabled and accessible.

Checking plug tasks

If a plug is no longer picking up messages or showing signs of life, it may have a background task that failed without restarting. For example, the Telegram plug has a long-poll task to receive updates, stored on the plug’s _receive attribute:

>>> plug._receive
<Task finished coro=<TelegramPlug._poll() done,
      defined at /opt/IMMP/immp/plug/>
      exception=RuntimeError('Catastrophic failure',)>

If the task is finished with an exception, call asyncio.Task.result() to get a traceback. A restart of the plug (Plug.stop(), Plug.start()) should get things moving again.

Checking the stream

If a plug is logging creation of its own messages, but the host stream isn’t receiving them, check if its messages are stuck in the queue, and refer to Host._stream:

>>> plug._queue.qsize()
>>> host._stream
<PlugStream: 0 done, 12 pending>

If all are pending, then every plug should be idle and awaiting a new message. If more than one are done for more than a moment, then one is likely wedged and holding up the others. You can try transplanting a new stream by re-calling Host.process():

>>> host._stream = host._process = None
>>> host._process = asyncio.ensure_future(host.process())

Replaying messages into a hook

First, fetch the message(s) you’re missing:

>>> msgs = (await host['my-channel'].history())[-3:]

Then feed them into a hook:

>>> for msg in msgs:
...     await host['sync'].on_receive(msg, msg, True)

For a SyncHook, this will send any as-yet-unsynced messages to linked channels.