Interfaces

Horetu makes interfaces to Python functions. Let us consider the following Python program, in a file called do_something.py.

#!/usr/bin/env python3
import horetu

def something(input_file, output_file, n_cores: int = 3):
    '''
    Do something to a file with several cores.
    '''
    # Pretend that something happens here.

To make it into a horetu interface, call the appropriate interface constructor.

Command-line interface

Create a command-line interface with the following additional line.

horetu.cli(something)

Then run this.

chmod +x do_something.py
./do_something.py -help

The help text will tell you how your new interface works.

horetu.cli(program, argv=['/home/tlevine/.local/bin/sphinx-build', '-b', 'dirhtml', '-d', '_build/doctrees', '.', '_build/dirhtml'], return_=False, handlers=None, freedesktop=False, exit_=<built-in function exit>, stdout=<_io.BufferedWriter name='<stdout>'>, stderr=<_io.BufferedWriter name='<stderr>'>)[source]
Parameters:
  • program (Program, callable, list, or dict) – The program for which to produce the interface
  • argv (list) – sys.argv by default
  • return (bool) – If this is True, simply return the result of the function; do not write stuff to exit_, stdout, or stderr.
  • exit (function) – sys.exit() by default
  • handlers (dict) – Mapping from content type to handler program
  • freedesktop (bool) – Fall back to xdg-open if a handler is not available
  • stdout (Binary file) – sys.stdout.buffer() by default
  • stderr (Binary file) – sys.stderr.buffer() by default

Web interfaces

horetu has two styles of web interface, a more basic style (py:func:horetu.wsgi_plain), and a style that produces forms. (py:func:horetu.wsgi_form). Add the this additional line to do_something.py to configure the former interface,

application = horetu.wsgi_plain(something)

or add this next line to configure the form interface.

application = horetu.wsgi_form(something)

Either way, you wind up with a Web Server Gateway Interface (WSGI) application that you can run it with a WSGI server. If you run this,

uwsgi --wsgi-file print-server.wsgi --plugin python3 --http-socket :9090

then you can open http://127.0.0.1:9090/?help to see how your web interface works.

You could also run the application in Apache mod_wsgi; add something like this to your apache configuration.

<VirtualHost *:80>
  WSGIScriptAlias / /srv/do_something.py process-group=something
  WSGIDaemonProcess something python-path=srv/do_something.py
  WSGIProcessGroup something

  ServerName 0.0.0.0
  ServerAdmin _@thomaslevine.com

  <Directory /srv>
    <Files do_something.py>
      Require all granted
    </Files>
  </Directory>
</VirtualHost>
horetu.wsgi_plain(program, debug=False)
horetu.wsgi_form(program, debug=False, template: <horetu.annotations.AnnotationFactory object at 0x29efb6d73c8> = <_io.StringIO object>, description=None, author=None)
Parameters:
  • f (Program, callable, list, or dict) – The program to produce the interface
  • name (str) – Name of the program, used for environment variable settings If it is None (the default), we attempt to get the name from the function.
  • debug (bool) – Print errors and debugging information to web pages?
  • template (File-like object) – Template file for form pages
Return type:

function

Returns:

WSGI application

Django management command interface

Suppose you have a Django application called in the directory “chainsaw”. Here is how you could configure the above function to be a management command called “replace-chain”.

mkdir -p chainsaw/management/commands
cp do_something.py chainsaw/management/commands/replace-chain.py
echo 'Command = horetu.django(something)' >> chainsaw/management/commands/replace-chain.py

Once you do that, you can run the command from a Django project that has the chainsaw application installed.

./manage.py chainsaw replace-chain
horetu.django(program, handlers=None, freedesktop=False)[source]

Produce a django management command to be assigned to <project>.management.commands.Command.

Parameters:
  • program (Program, callable, list, or dict) – The program for which to produce the interface
  • handlers (dict) – Mapping from content type to handler program
  • freedesktop (bool) – Fall back to xdg-open if a handler is not available

IRC interface

The IRC interface is like the command-line interface but over IRC.

horetu.irc(something, server='irc.freenode.net',
           channel='##horetu', nick='something')

Then you can send messages to user “something” in the ##horetu channel, and that user will respond as if you were typing in a shell and running the same function but with :py:func:horetu.cli. This calls :py:func:horetu.cli internally.

You may want to configure the server from the command line; you can do that like so.

horetu.irc(something, server='irc.freenode.net',
           channel='##horetu', nick='something')
           cli=True)

This starts a command-line interface (:py:func:horetu.cli) that accepts arguments for “server”, “channel”, &c., falling back to the defaults that you provided when an argument is not provided. This interface then runs :py:func:horetu.irc (which internally runs :py:func:horetu.cli again, as I mentioned above).

horetu.irc(program, server, channel, nick=None, *, port: <horetu.annotations.AnnotationFactory object at 0x29efb6d70f0> = 6667, cli=False, only_mentions=False, debug=False)[source]

Render a Python function as an IRC bot.

Parameters:
  • program (Program, callable, list, or dict) – The program for which to produce the interface
  • server – IRC server to connect to
  • channel – IRC channel to join
  • nick – IRC handle
  • port (int) – IRC server port
  • cli (bool) – If True, render a command-line program that starts an IRC bot with the configured values as defaults.

Configuration files

The function that you pass to the horetu interface is implicitly converted to a horetu.Program object. If you set this explicitly, you can also set configuration files.

application = horetu.wsgi_plain(horetu.Program(something, '~/.something.conf`))

For unspecified arguments, the program falls back, first, to the options set in configuration files, and, second, to any defaults that are set in the Python function.

horetu.Program(function, *configurations, name=None)[source]
horetu.Configuration(*args, **kwargs)[source]

Configuration file interfaces

You can produce a configuration file with the default options implied by the function’s keyword arguments. This could be useful in a command that does the initial configuration for your program.

horetu.config_default(horetu.Program(something,
    '/etc/something.conf', '~/.something.conf'))

You can also do the opposite: Run a program with only the options specified in a configuration file.

horetu.config_run(horetu.Program(something, '~/.something.conf'))
horetu.config_default(program, with_sections=False)

Write a configuration file with a program’s defaults.

Parameters:
  • program (Program, callable, list, or dict) – The program for which to produce the interface,
  • with_sections (bool) – Give each subcommand its own namespace rather than putting all of the arguments in a global namespace.
horetu.config_run(program, section=())

Run a program, applying arguments from any included configuration files.

Parameters:
  • program (Program, callable, list, or dict) – The program for which to produce the interface,
  • section (Iterable of tuple of str) – Subcommand to run.