{ "cells": [ { "cell_type": "raw", "id": "6c469b24-ecf2-489b-9663-279aba5d9930", "metadata": {}, "source": [ "---\n", "title: Generate Click command line options dynamically from class arguments\n", "date: 2022-10-26\n", "categories:\n", " - python\n", "---" ] }, { "cell_type": "markdown", "id": "1e0d1df8-f595-4107-9e9f-68f143ba0fbd", "metadata": {}, "source": [ "In this application I need to use command line options to create class objects. In order not to repeat all class arguments in the [Click](https://click.palletsprojects.com) configuration, I created a simple function to dynamically create Click options. [See the source notebook of this page on Github](https://github.com/zonca/zonca.dev/blob/main/posts/2022-10-26-click-commandline-class-arguments.ipynb)" ] }, { "cell_type": "code", "execution_count": 1, "id": "287c0db4-a4a7-4d95-b39e-44ebb5d23a94", "metadata": {}, "outputs": [], "source": [ "import click\n", "import inspect" ] }, { "cell_type": "markdown", "id": "cbc46b54-d995-46ae-be01-07cc4dedd0a2", "metadata": {}, "source": [ "We have 2 classes, we want to pass the class name as argument to the command line tool and then all its arguments, for example:\n", "\n", " createclass aclass --a somestring --b 6\n", " \n", "We also use Python `typing` so we can pass that to Click." ] }, { "cell_type": "code", "execution_count": 2, "id": "a511cc4c-01a7-4f6c-9c67-cc6bd01d1204", "metadata": {}, "outputs": [], "source": [ "class AClass:\n", " def __init__(\n", " self,\n", " a: str,\n", " b: int):\n", " pass" ] }, { "cell_type": "code", "execution_count": 3, "id": "04d8f528-b288-417e-89d9-0b518c1f5881", "metadata": {}, "outputs": [], "source": [ "class BClass:\n", " def __init__(\n", " self,\n", " c: float,\n", " d: bool,\n", " under_score: str):\n", " pass" ] }, { "cell_type": "code", "execution_count": 4, "id": "31ebd324-d112-41df-a0a4-61e8042c2235", "metadata": {}, "outputs": [], "source": [ "def options_from_class(cls):\n", " def decorator(f):\n", " for par in inspect.signature(cls.__init__).parameters.values():\n", " if par.name not in [\"self\"]:\n", " click.option(\"--\" + par.name, required=True, type=par.annotation)(f)\n", " return f\n", " return decorator" ] }, { "cell_type": "code", "execution_count": 5, "id": "c73d97ac-791b-4bd8-895c-3b31035ec455", "metadata": {}, "outputs": [], "source": [ "@click.group()\n", "def cli():\n", " pass" ] }, { "cell_type": "code", "execution_count": 6, "id": "e1303772-eb24-46ba-952e-3eb59f0f519e", "metadata": {}, "outputs": [], "source": [ "@cli.command()\n", "@options_from_class(AClass)\n", "def aclass(**kwargs):\n", " click.echo('kwargs: {}'.format(kwargs))\n", " ac = AClass(**kwargs)" ] }, { "cell_type": "code", "execution_count": 7, "id": "aef1bec9-7183-4abd-b486-f1760e178dd8", "metadata": {}, "outputs": [], "source": [ "@cli.command()\n", "@options_from_class(BClass)\n", "def bclass(**kwargs):\n", " click.echo('kwargs: {}'.format(kwargs))\n", " bc = BClass(**kwargs)" ] }, { "cell_type": "code", "execution_count": null, "id": "5ba7946c-c95b-43aa-a40d-66b6ec96c325", "metadata": {}, "outputs": [], "source": [ "if __name__ == '__main__':\n", " cli()" ] }, { "cell_type": "markdown", "id": "9df86a13-a079-4826-9ff2-6961a73360d0", "metadata": {}, "source": [ "Convert the Notebook to a Python script with:\n", "\n", " jupyter nbconvert click-commandline-class-arguments.ipynb --to python\n", " \n", "Finally test at the command line:\n", "\n", "```\n", "$ python click-commandline-class-arguments.py aclass --help\n", "Usage: click-commandline-class-arguments.py aclass [OPTIONS]\n", "\n", "Options:\n", " --b INTEGER [required]\n", " --a TEXT [required]\n", " --help Show this message and exit.\n", "```\n", "\n", "```\n", "python click-commandline-class-arguments.py bclass --help\n", "Usage: click-commandline-class-arguments.py bclass [OPTIONS]\n", "\n", "Options:\n", " --under_score TEXT [required]\n", " --d BOOLEAN [required]\n", " --c FLOAT [required]\n", " --help Show this message and exit.\n", "```\n", "\n", "```\n", "$ python click-commandline-class-arguments.py bclass --d true --c 4.5 --under_score works\n", "kwargs: {'d': True, 'c': 4.5, 'under_score': 'works'}\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "microroot", "language": "python", "name": "microroot" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.13" } }, "nbformat": 4, "nbformat_minor": 5 }