Packaging an installed command

Ship a Koskari application as a pip-installable command. Users run mytool from a shell; the sources stay in .ki files under a declared Python package.

How it works

Koskari defines a koskari_scripts entry point group, the counterpart of console_scripts for Koskari programs. At build time the Koskari setuptools plugin:

  • writes a stub for each command into the wheel, through install_scripts, that calls run_resolved_script(dist==version, name)

  • adds *.ki and *.kio to package_data for each package listed in packages=

  • sets zip_safe = False so the installed sources stay discoverable

At install time pip copies the stub into bin/mytool. At run time the stub resolves the koskari_scripts target from that package’s dist-info and runs it under the Koskari async host, so the application needs no asyncio.run of its own.

A minimal package

myapp/
  setup.cfg
  setup.py
  pyproject.toml
  mypkg/
    __init__.py
    cli.ki

setup.cfg carries the metadata and the entry point:

[metadata]
name = myapp
version = 0.1.0
description = My Koskari application

[options]
packages =
    mypkg
install_requires =
    koskari
python_requires = >=3.9

[options.entry_points]
koskari_scripts =
    mytool = mypkg.cli:main

setup.py is the minimal shim:

from setuptools import setup

setup()

pyproject.toml lists koskari as a build requirement so the plugin runs:

[build-system]
requires = ["setuptools>=61", "koskari"]
build-backend = "setuptools.build_meta"

mypkg/cli.ki exposes main with the console_scripts contract: no parameters, arguments read from sys.argv, an integer exit code returned:

(import sys)
(import-from argparse ArgumentParser)

def main []:
  (var [prog *: args] sys.argv)
  ;; parse args, run the program, return an exit code
  0

An argv parameter with a default of sys.argv is also accepted, since main is called with no arguments.

mypkg/__init__.py runs import koskari, which turns the .ki import hook on when the package loads, including in environments where Koskari’s site .pth bootstrap is absent, as it is in editable installs:

import koskari

No Python main function and no asyncio.run wrapper is needed.

Build and install

From the application directory:

pip install .

When koskari is available only locally, the build environment must be able to import it: install koskari editable first, or disable build isolation for that build.

After the install, the distribution’s entry points list the command:

[koskari_scripts]
mytool = mypkg.cli:main

The installed bin/mytool calls run_resolved_script('myapp==...', 'mytool'), and mytool --help runs it.

Rules

Topic

Rule

entry value

module:callable, for example mypkg.cli:main, or a module alone, for example mypkg.cli, meaning that module’s entry point

entry callable

called with no arguments; read sys.argv; the return value is the exit status, as for an @entry-point function

return value

an integer exit code, None for 0, or an awaitable producing one

packages=

declare it; the plugin does not guess package names

package_data

omit *.ki and *.kio; the plugin adds them

zip_safe

omit it; the plugin sets False when koskari_scripts is present

name collisions

do not register the same command under console_scripts

CI validation

koskari.extras.setuptools.validate_koskari_scripts checks entry names and values on a Distribution

The repository’s examples/life is a complete application of this shape: package koslife, command koslife, entry koslife.cli:main, a curses program.

The bundled icon

Koskari ships its apple mark inside the package, so an application can use it as a window icon or favicon without vendoring the artwork; koskari.assets is the API.

See also