From 34dbaea5eb8cb792883e8d2beedf13960132fbde Mon Sep 17 00:00:00 2001 From: Edvard Rejthar Date: Tue, 10 Sep 2024 17:16:59 +0200 Subject: [PATCH] Deployed 33ea7f6 with MkDocs version: 1.6.1 --- 404.html | 20 +++++++++ Mininterface/index.html | 20 +++++++++ Overview/index.html | 87 ++++++++++++++++++++++++++++++++++++++++ Standalone/index.html | 20 +++++++++ Tag/index.html | 20 +++++++++ Validation/index.html | 21 ++++++++++ index.html | 24 ++++++++++- run/index.html | 22 +++++++++- search/search_index.json | 2 +- 9 files changed, 232 insertions(+), 4 deletions(-) diff --git a/404.html b/404.html index 94c291b..33602cb 100644 --- a/404.html +++ b/404.html @@ -227,6 +227,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • diff --git a/Mininterface/index.html b/Mininterface/index.html index 3cf2ee1..2763958 100644 --- a/Mininterface/index.html +++ b/Mininterface/index.html @@ -236,6 +236,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • diff --git a/Overview/index.html b/Overview/index.html index c43af69..887cac3 100644 --- a/Overview/index.html +++ b/Overview/index.html @@ -9,6 +9,10 @@ + + + + @@ -230,6 +234,89 @@ + + + + +
  • + + + + + + + + + + + + Overview + + + + + + + + + +
  • + + + + + + +
  • diff --git a/Standalone/index.html b/Standalone/index.html index 2b4856f..6414611 100644 --- a/Standalone/index.html +++ b/Standalone/index.html @@ -229,6 +229,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • diff --git a/Tag/index.html b/Tag/index.html index 1650d9f..ce24409 100644 --- a/Tag/index.html +++ b/Tag/index.html @@ -236,6 +236,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • diff --git a/Validation/index.html b/Validation/index.html index eedbf87..15665a4 100644 --- a/Validation/index.html +++ b/Validation/index.html @@ -236,6 +236,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • @@ -778,6 +798,7 @@

    transform + bool
    diff --git a/index.html b/index.html index b09f072..1ac8366 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,7 @@ - + @@ -301,6 +301,26 @@ +
  • + + + + + Overview + + + + +
  • + + + + + + + + +
  • @@ -538,7 +558,7 @@

    Installation

    pip install mininterface
     

    Docs

    -

    See the docs at https://cz-nic.github.io/mininterface/.

    +

    See the docs overview at https://cz-nic.github.io/mininterface/.

    Examples

    This is a complex example.

    from typing import Annotated
    diff --git a/run/index.html b/run/index.html
    index 56b7fee..06dd511 100644
    --- a/run/index.html
    +++ b/run/index.html
    @@ -9,7 +9,7 @@
           
           
           
    -        
    +        
           
           
             
    @@ -234,6 +234,26 @@
           
       
       
    +  
    +  
    +    
  • + + + + + Overview + + + + +
  • + + + + + + + diff --git a/search/search_index.json b/search/search_index.json index dd280b8..b8ad295 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Mininterface \u2013 access to GUI, TUI, CLI and config files","text":"

    Write the program core, do not bother with the input/output.

    Check out the code, which is surprisingly short, that displays such a window or its textual fallback.

    from dataclasses import dataclass\nfrom mininterface import run\n\n@dataclass\nclass Env:\n    \"\"\"Set of options.\"\"\"\n\n    test: bool = False\n    \"\"\" My testing flag \"\"\"\n\n    important_number: int = 4\n    \"\"\" This number is very important \"\"\"\n\nif __name__ == \"__main__\":\n    env = run(Env, prog=\"My application\").env\n    # Attributes are suggested by the IDE\n    # along with the hint text 'This number is very important'.\n    print(env.important_number)\n
    "},{"location":"#you-got-cli","title":"You got CLI","text":"

    It was all the code you need. No lengthy blocks of code imposed by an external dependency. Besides the GUI/TUI, you receive powerful YAML-configurable CLI parsing.

    $ ./hello.py\nusage: My application [-h] [--test | --no-test] [--important-number INT]\n\nSet of options.\n\n\u256d\u2500 options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 -h, --help              show this help message and exit            \u2502\n\u2502 --test, --no-test       My testing flag (default: False)           \u2502\n\u2502 --important-number INT  This number is very important (default: 4) \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n
    "},{"location":"#you-got-config-file-management","title":"You got config file management","text":"

    Loading config file is a piece of cake. Alongside program.py, put program.yaml and put there some of the arguments. They are seamlessly taken as defaults.

    important_number: 555\n
    "},{"location":"#you-got-dialogues","title":"You got dialogues","text":"

    Check out several useful methods to handle user dialogues. Here we bound the interface to a with statement that redirects stdout directly to the window.

    with run(Env) as m:\n    print(f\"Your important number is {m.env.important_number}\")\n    boolean = m.is_yes(\"Is that alright?\")\n

    "},{"location":"#contents","title":"Contents","text":"
    • Mininterface \u2013 GUI, TUI, CLI and config
    • Background
    • Installation
    • Docs
    • Examples
    "},{"location":"#background","title":"Background","text":"

    Wrapper between the tyro argparse replacement and tkinter_form that converts dicts into a GUI.

    Writing a small and useful program might be a task that takes fifteen minutes. Adding a CLI to specify the parameters is not so much overhead. But building a simple GUI around it? HOURS! Hours spent on researching GUI libraries, wondering why the Python desktop app ecosystem lags so far behind the web world. All you need is a few input fields validated through a clickable window... You do not deserve to add hundred of lines of the code just to define some editable fields. Mininterface is here to help.

    The config variables needed by your program are kept in cozy dataclasses. Write less! The syntax of tyro does not require any overhead (as its argparse alternatives do). You just annotate a class attribute, append a simple docstring and get a fully functional application: * Call it as program.py --help to display full help. * Use any flag in CLI: program.py --test causes env.test be set to True. * The main benefit: Launch it without parameters as program.py to get a full working window with all the flags ready to be edited. * Running on a remote machine? Automatic regression to the text interface.

    "},{"location":"#installation","title":"Installation","text":"

    Install with a single command from PyPi.

    pip install mininterface\n
    "},{"location":"#docs","title":"Docs","text":"

    See the docs at https://cz-nic.github.io/mininterface/.

    "},{"location":"#examples","title":"Examples","text":"

    This is a complex example.

    from typing import Annotated\nfrom dataclasses import dataclass\nfrom mininterface.validators import not_empty\nfrom mininterface import run, Tag, Validation\n\n@dataclass\nclass NestedEnv:\n  another_number: int = 7\n  \"\"\" This field is nested \"\"\"\n\n@dataclass\nclass Env:\n  nested_config: NestedEnv\n\n  my_number: int = 5\n  \"\"\" This is just a dummy number \"\"\"\n\n  my_string: str = \"Hello\"\n  \"\"\" A dummy string \"\"\"\n\n  my_flag: bool = False\n  \"\"\" Checkbox test \"\"\"\n\n  my_validated: Annotated[str, Validation(not_empty)] = \"hello\"\n  \"\"\" A validated field \"\"\"\n\nm = run(Env, title=\"My program\")\n# See some values\nprint(m.env.nested_config.another_number)  # 7\nprint(m.env)\n# Env(nested_config=NestedEnv(another_number=7), my_number=5, my_string='Hello', my_flag=False, my_validated='hello')\n\n# Edit values in a dialog\nm.form()\n
    "},{"location":"Mininterface/","title":"Mininterface","text":"

    Bases: Generic[EnvClass]

    The base interface. You get one through mininterface.run which fills CLI arguments and config file to mininterface.env or you can create one directly (without benefiting from the CLI parsing).

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.env","title":"env: EnvClass = _env or SimpleNamespace() instance-attribute","text":"

    Parsed arguments, fetched from cli Contains whole configuration (previously fetched from CLI and config file).

    $ program.py --number 10\n
    from dataclasses import dataclass\nfrom mininterface import run\n\n@dataclass\nclass Env:\n    number: int = 3\n    text: str = \"\"\n\nm = run(Env)\nprint(m.env.number)  # 10\n
    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.__enter__","title":"__enter__()","text":"

    When used in the with statement, the GUI window does not vanish between dialogs and it redirects the stdout to a text area.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.alert","title":"alert(text)","text":"

    Prompt the user to confirm the text.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.ask","title":"ask(text)","text":"

    Prompt the user to input a text.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.ask_number","title":"ask_number(text)","text":"

    Prompt the user to input a number. Empty input = 0.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.form","title":"form(form=None, title='')","text":"

    Prompt the user to fill up whole form.

    Parameters:

    Name Type Description Default form FormDictOrEnv | None

    Dict of {labels: default value}. The form widget infers from the default value type. The dict can be nested, it can contain a subgroup. The default value might be mininterface.Tag that allows you to add descriptions. If None, the self.env is being used as a form, allowing the user to edit whole configuration. (Previously fetched from CLI and config file.) A checkbox example: {\"my label\": Tag(True, \"my description\")}

    None title str

    Optional form title

    ''"},{"location":"Mininterface/#mininterface.mininterface.Mininterface.is_yes","title":"is_yes(text)","text":"

    Display confirm box, focusing yes.

    m = run()\nprint(m.is_yes(\"Is that alright?\"))  # True/False\n

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.is_no","title":"is_no(text)","text":"

    Display confirm box, focusing no.

    "},{"location":"Mininterface/#all-possible-interfaces","title":"All possible interfaces","text":"

    Several interfaces exist:

    • Mininterface \u2013 The base interface. Does not require any user input and hence is suitable for headless testing.
    • GuiInterface \u2013 A tkinter window.
    • TuiInterface \u2013 An interactive terminal.
    • TextualInterface \u2013 If textual installed, rich interface is used.
    • TextInterface \u2013 Plain text only interface with no dependency as a fallback.
    • ReplInterface \u2013 A debug terminal. Invokes a breakpoint after every dialog.

    Normally, you get an interface through mininterface.run but if you do not wish to parse CLI and config file, you can invoke one directly.

    with TuiInterface(\"My program\") as m:\n    number = m.ask_number(\"Returns number\")\n
    "},{"location":"Overview/","title":"Overview","text":""},{"location":"Overview/#basic-usage","title":"Basic usage","text":"

    Use a common dataclass, a Pydantic BaseModel or an attrs model to store the configuration. Wrap it to the run method that returns an interface m. Access the configuration via m.env or use it to prompt the user m.is_yes(\"Is that alright?\"). To do any advanced things, stick the value to a powerful Tag. For a validation only, use its Validation alias.

    "},{"location":"Overview/#supported-types","title":"Supported types","text":"

    Various types are supported: scalars, functions, well-known objects (Path), iterables, custom classes.

    Take a look how it works with the variables organized in a dataclass:

    from dataclasses import dataclass\nfrom pathlib import Path\n\nfrom mininterface import run\n\n\n@dataclass\nclass Env:\n    my_number: int = 1\n    \"\"\" A dummy number \"\"\"\n    my_boolean: bool = True\n    \"\"\" A dummy boolean \"\"\"\n    my_path: Path = Path(\"/tmp\")\n    \"\"\" A dummy path \"\"\"\n\n\nm = run(Env)  # m.env contains an Env instance\nm.form()  # Prompt a dialog; m.form() without parameter edits m.env\nprint(m.env)\n# Env(my_number=1, my_boolean=True, my_path=PosixPath('/tmp'),\n#  my_point=<__main__.Point object at 0x7ecb5427fdd0>)\n

    Variables organized in a dict:

    Along scalar types, there is (basic) support for common iterables or custom classes.

    from mininterface import run\n\nclass Point:\n    def __init__(self, i: int):\n        self.i = i\n\n    def __str__(self):\n        return str(self.i)\n\n\nvalues = {\"my_number\": 1,\n          \"my_list\": [1, 2, 3],\n          \"my_point\": Point(10)\n          }\n\nm = run()\nm.form(values)  # Prompt a dialog\nprint(values)  # {'my_number': 2, 'my_list': [2, 3], 'my_point': <__main__.Point object...>}\nprint(values[\"my_point\"].i)  # 100\n

    "},{"location":"Overview/#nested-configuration","title":"Nested configuration","text":"

    You can easily nest the configuration. (See also Tyro Hierarchical Configs).

    Just put another dataclass inside the config file:

    @dataclass\nclass FurtherConfig:\n    token: str\n    host: str = \"example.org\"\n\n@dataclass\nclass Config:\n    further: FurtherConfig\n\n...\nprint(config.further.host)  # example.org\n

    A subset might be defaulted in YAML:

    further:\n  host: example.com\n

    Or by CLI:

    $./program.py --further.host example.net\n
    "},{"location":"Standalone/","title":"Standalone","text":"

    When invoked directly, it creates simple GUI dialogs.

    $ mininterface  --help\nusage: Mininterface [-h] [OPTIONS]\n\nSimple GUI dialog. Outputs the value the user entered.\n\n\u256d\u2500 options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 -h, --help              show this help message and exit                                   \u2502\n\u2502 --alert STR             Display the OK dialog with text. (default: '')                    \u2502\n\u2502 --ask STR               Prompt the user to input a text. (default: '')                    \u2502\n\u2502 --ask-number STR        Prompt the user to input a number. Empty input = 0. (default: '') \u2502\n\u2502 --is-yes STR            Display confirm box, focusing yes. (default: '')                  \u2502\n\u2502 --is-no STR             Display confirm box, focusing no. (default: '')                   \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n

    You can fetch a value to i.e. a bash script.

    $ mininterface  --ask-number \"What's your age?\"  # GUI window invoked\n18\n
    "},{"location":"Tag/","title":"Tag","text":"

    Wrapper around a value that encapsulates a description, validation etc. When you provide a value to an interface, you may instead use this object.

    Bridge between the input values and a UI widget. The widget is created with the help of this object, then transforms the value back (str to int conversion etc).

    "},{"location":"Tag/#mininterface.Tag.val","title":"val: TagValue = None","text":"

    The value wrapped by Tag.

    from mininterface import run, Tag\n\ntag = Tag(True, \"This is my boolean\", bool)\nm = run()\nm.form({\"My boolean\": tag})\nprint(tag.val)  # True/False\nprint()\n

    The encapsulated value is True, tag.description is 'This is my boolean', tag.annotation is bool and 'My boolean' is used as tag.name.

    "},{"location":"Tag/#mininterface.Tag.description","title":"description: str = ''","text":"

    The description displayed in the UI.

    "},{"location":"Tag/#mininterface.Tag.annotation","title":"annotation: type | None = None","text":"

    Used for validation (ex. to convert an empty string to None). If not set, will be determined automatically from the val type.

    "},{"location":"Tag/#mininterface.Tag.name","title":"name: str | None = None","text":"

    Name displayed in the UI.

    "},{"location":"Tag/#mininterface.Tag.validation","title":"validation: Callable[[Tag], ValidationResult | tuple[ValidationResult, TagValue]] | None = None","text":"

    When the user submits the form, the values are validated (and possibly transformed) with a callback function. If the validation fails, user is prompted to edit the value. Return True if validation succeeded or False or an error message when it failed.

    ValidationResult is a bool or the error message (that implicitly means it has failed).

    def check(tag: Tag):\n    if tag.val < 10:\n        return \"The value must be at least 10\"\nm.form({\"number\", Tag(12, validation=check)})\n

    Either use a custom callback function or mininterface.validators.

    from mininterface.validators import not_empty\nm.form({\"number\", Tag(\"\", validation=not_empty)})\n# User cannot leave the field empty.\n

    You may use the validation in a type annotation.

    from mininterface import Tag, Validation\n@dataclass\nclass Env:\n    my_text: Annotated[str, Validation(not_empty) = \"will not be emtpy\"\n\n    # which is an alias for:\n    # my_text: Annotated[str, Tag(validation=not_empty)] = \"will not be emtpy\"\n

    NOTE Undocumented feature, we can return tuple [ValidationResult, FieldValue] to set the self.val.

    "},{"location":"Tag/#mininterface.Tag.choices","title":"choices: list[str] | None = None","text":"

    TODO

    "},{"location":"Tag/#mininterface.Tag.original_val","title":"original_val = None","text":"

    Read only. The original value, preceding UI change. Handy while validating.

    def check(tag.val):\n    if tag.val != tag.original_val:\n        return \"You have to change the value.\"\nm.form({\"number\", Tag(8, validation=check)})\n
    "},{"location":"Tag/#mininterface.Tag.error_text","title":"error_text = None","text":"

    Read only. Error text if type check or validation fail and the UI has to be revised

    "},{"location":"Tag/#mininterface.Tag.update","title":"update","text":"

    UI value \u2192 Tag value \u2192 original value. (With type conversion and checks.)

    Parameters:

    Name Type Description Default ui_value TagValue

    The value as it has been updated in a UI. Update accordingly the value in the original linked dict/object the mininterface was invoked with.

    Validates the type and do the transformation. (Ex: Some values might be nulled from \"\".)

    required

    Returns:

    Type Description bool

    bool, whether the value is alright or whether the revision is needed.

    "},{"location":"Tag/#helper-types","title":"Helper types","text":""},{"location":"Tag/#mininterface.tag.ValidationResult","title":"ValidationResult = bool | ErrorMessage","text":"

    Callback validation result is either boolean or an error message.

    "},{"location":"Tag/#mininterface.tag.ErrorMessage","title":"ErrorMessage = TypeVar('ErrorMessage')","text":"

    A string, callback validation error message.

    "},{"location":"Tag/#mininterface.tag.TagValue","title":"TagValue = TypeVar('TagValue')","text":"

    Any value.

    "},{"location":"Validation/","title":"Validation","text":"

    We recommend to use the dataclass and validate with the Annotated keyword.

    from typing import Annotated\nfrom mininterface.validators import not_empty\nfrom mininterface import Validation\n\n@dataclass\nclass Env:\n    test: Annotated[str, Validation(not_empty)] = \"hello\"\n

    Under the hood, this is just a Tag.

    @dataclass\nclass Env:\n    test: Annotated[str, Tag(validation=not_empty)] = \"hello\"\n

    Why we used it in an Annotated statement? To preserve the date type.

    @dataclass\nclass Env:\n    my_string: Tag = Tag(\"hello\", validation=not_empty)\n\nm = run(Env)\nprint(type(m.env.my_string))  # Tag\nprint(m.env.my_string.val)  # hello\n
    "},{"location":"Validation/#validation-alias","title":"Validation alias","text":"

    Alias to Tag(validation=...)

    from mininterface import Tag, Validation\n@dataclass\nclass Env:\n    my_text: Annotated[str, Validation(not_empty) = \"will not be emtpy\"\n\n    # which is an alias for:\n    # my_text: Annotated[str, Tag(validation=not_empty)] = \"will not be emtpy\"\n

    Parameters:

    Name Type Description Default check Callable[[Tag], ValidationResult | tuple[ValidationResult, TagValue]]

    Callback function.

    required"},{"location":"Validation/#validators","title":"validators","text":"

    Functions suitable for Tag validation. When the user submits a value whose validation fails, they are prompted to edit the value.

    m = run()\nmy_dict = m.form({\"my_text\", Tag(\"\", validation=validators.not_empty)})\nmy_dict[\"my_text\"]  # You can be sure the value is not empty here.\n

    Note that alternatively to this module, you may validate with Pydantic or an attrs model.

    from pydantic import BaseModel, Field\n\nclass MyModel(BaseModel):\n    restrained: str = Field(default=\"hello\", max_length=5)\n
    import attr\nfrom attr.validators import max_len\n\n@attr.s\nclass AttrsModel:\n    restrained: str = attr.ib(default=\"hello\", validator=max_len(5))\n
    "},{"location":"Validation/#mininterface.validators.not_empty","title":"not_empty(tag)","text":"

    Assures that Tag the user has written a value and did not let the field empty.

    from mininterface import Tag, validators, run\n\nm = run()\nm.form({\"my_text\": Tag(\"\", validation=validators.not_empty)})\n# User cannot leave the string field empty.\n

    When submitting an empty value, a warning appears:

    Note that for Path, an empty string is converted to an empty Path('.'), hence '.' too is considered as an empty input and the user is not able to set '.' as a value. This does not seem to me as a bad behaviour as in CLI you clearly see the CWD, whereas in a UI the CWD is not evident.

    Parameters:

    Name Type Description Default tag Tag required"},{"location":"Validation/#mininterface.validators.limit","title":"limit(maxOrMin=None, max_=None, lt=None, gt=None, transform=False)","text":"

    Limit a number range or a string length.

    Either use as limit(maximum) or limit(minimum, maximum).

    Parameters:

    Name Type Description Default maximum int

    limit(maximum) \u2013 from zero (including) to maximum (including)

    required minimum int

    limit(minimum, maximum) \u2013 From minimum (including) to maximum (including)

    required lt float | None

    lesser than

    None gt float | None

    greater than

    None transform

    If the value is not withing the limit, transform it to a boundary.

    from mininterface import run, Tag\nfrom mininterface.validators import limit\n\nm = run()\nm.form({\"my_number\": Tag(2, validation=limit(1, 10, transform=True))})\n# Put there '50' \u2192 transformed to 10 and dialog reappears\n# with 'Value must be between 1 and 10.'\n

    False"},{"location":"run/","title":"Run","text":"

    The main access, start here. Wrap your configuration dataclass into run to access the interface. An interface is chosen automatically, with the preference of the graphical one, regressed to a text interface for machines without display. Besides, if given a configuration dataclass, the function enriches it with the CLI commands and possibly with the default from a config file if such exists. It searches the config file in the current working directory, with the program name ending on .yaml, ex: program.py will fetch ./program.yaml.

    Parameters:

    Name Type Description Default env_class Type[EnvClass] | None

    Dataclass with the configuration. Their values will be modified with the CLI arguments.

    None ask_on_empty_cli bool

    If program was launched with no arguments (empty CLI), invokes self.form() to edit the fields. (Withdrawn when ask_for_missing happens.)

    @dataclass\nclass Env:\nnumber: int = 3\ntext: str = \"\"\nm = run(Env, ask_on_empty=True)\n

    $ program.py  #  omitting all parameters\n# Dialog for `number` and `text` appears\n$ program.py --number 3\n# No dialog appears\n
    False title str

    The main title. If not set, taken from prog or program name.

    '' config_file Path | str | bool

    File to load YAML to be merged with the configuration. You do not have to re-define all the settings in the config file, you can choose a few. If set to True (default), we try to find one in the current working dir, whose name stem is the same as the program's. Ex: program.py will search for program.yaml. If False, no config file is used.

    True add_verbosity bool

    Adds the verbose flag that automatically sets the level to logging.INFO (-v) or logging.DEBUG (-vv).

    import logging\nlogger = logging.getLogger(__name__)\n\nm = run(Env, add_verbosity=True)\nlogger.info(\"Info shown\") # needs `-v` or `--verbose`\nlogger.debug(\"Debug not shown\")  # needs `-vv`\n# $ program.py --verbose\n# Info shown\n
    $ program.py --verbose\nInfo shown\n
    True ask_for_missing bool

    If some required fields are missing at startup, we ask for them in a UI instead of program exit.

    @dataclass\nclass Env:\nrequired_number: int\nm = run(Env, ask_for_missing=True)\n
    $ program.py  # omitting --required-number\n# Dialog for `required_number` appears\n
    True interface Type[Mininterface]

    Which interface to prefer. By default, we use the GUI, the fallback is the TUI. See the full list of possible interfaces.

    GuiInterface or TuiInterface Kwargs

    The same as for argparse.ArgumentParser.

    Returns:

    Type Description Mininterface[EnvClass]

    An interface, ready to be used.

    You cay context manager the function by a with statement. The stdout will be redirected to the interface (ex. a GUI window). TODO add wrap example

    Undocumented experimental: The env_class may be a function as well. We invoke its parameters. However, as Mininterface.env stores the output of the function instead of the Argparse namespace, methods like Mininterface.form(None) (to ask for editing the env values) will work unpredictibly. Also, the config file seems to be fetched only for positional (missing) parameters, and ignored for keyword (filled) parameters. It seems to be this is the tyro's deal and hence it might start working any time. If not, we might help it this way: if isinstance(config, FunctionType): config = lambda: config(**kwargs[\"default\"])

    Undocumented experimental: default keyword argument for tyro may serve for default values instead of a config file.

    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Mininterface \u2013 access to GUI, TUI, CLI and config files","text":"

    Write the program core, do not bother with the input/output.

    Check out the code, which is surprisingly short, that displays such a window or its textual fallback.

    from dataclasses import dataclass\nfrom mininterface import run\n\n@dataclass\nclass Env:\n    \"\"\"Set of options.\"\"\"\n\n    test: bool = False\n    \"\"\" My testing flag \"\"\"\n\n    important_number: int = 4\n    \"\"\" This number is very important \"\"\"\n\nif __name__ == \"__main__\":\n    env = run(Env, prog=\"My application\").env\n    # Attributes are suggested by the IDE\n    # along with the hint text 'This number is very important'.\n    print(env.important_number)\n
    "},{"location":"#you-got-cli","title":"You got CLI","text":"

    It was all the code you need. No lengthy blocks of code imposed by an external dependency. Besides the GUI/TUI, you receive powerful YAML-configurable CLI parsing.

    $ ./hello.py\nusage: My application [-h] [--test | --no-test] [--important-number INT]\n\nSet of options.\n\n\u256d\u2500 options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 -h, --help              show this help message and exit            \u2502\n\u2502 --test, --no-test       My testing flag (default: False)           \u2502\n\u2502 --important-number INT  This number is very important (default: 4) \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n
    "},{"location":"#you-got-config-file-management","title":"You got config file management","text":"

    Loading config file is a piece of cake. Alongside program.py, put program.yaml and put there some of the arguments. They are seamlessly taken as defaults.

    important_number: 555\n
    "},{"location":"#you-got-dialogues","title":"You got dialogues","text":"

    Check out several useful methods to handle user dialogues. Here we bound the interface to a with statement that redirects stdout directly to the window.

    with run(Env) as m:\n    print(f\"Your important number is {m.env.important_number}\")\n    boolean = m.is_yes(\"Is that alright?\")\n

    "},{"location":"#contents","title":"Contents","text":"
    • Mininterface \u2013 GUI, TUI, CLI and config
    • Background
    • Installation
    • Docs
    • Examples
    "},{"location":"#background","title":"Background","text":"

    Wrapper between the tyro argparse replacement and tkinter_form that converts dicts into a GUI.

    Writing a small and useful program might be a task that takes fifteen minutes. Adding a CLI to specify the parameters is not so much overhead. But building a simple GUI around it? HOURS! Hours spent on researching GUI libraries, wondering why the Python desktop app ecosystem lags so far behind the web world. All you need is a few input fields validated through a clickable window... You do not deserve to add hundred of lines of the code just to define some editable fields. Mininterface is here to help.

    The config variables needed by your program are kept in cozy dataclasses. Write less! The syntax of tyro does not require any overhead (as its argparse alternatives do). You just annotate a class attribute, append a simple docstring and get a fully functional application: * Call it as program.py --help to display full help. * Use any flag in CLI: program.py --test causes env.test be set to True. * The main benefit: Launch it without parameters as program.py to get a full working window with all the flags ready to be edited. * Running on a remote machine? Automatic regression to the text interface.

    "},{"location":"#installation","title":"Installation","text":"

    Install with a single command from PyPi.

    pip install mininterface\n
    "},{"location":"#docs","title":"Docs","text":"

    See the docs overview at https://cz-nic.github.io/mininterface/.

    "},{"location":"#examples","title":"Examples","text":"

    This is a complex example.

    from typing import Annotated\nfrom dataclasses import dataclass\nfrom mininterface.validators import not_empty\nfrom mininterface import run, Tag, Validation\n\n@dataclass\nclass NestedEnv:\n  another_number: int = 7\n  \"\"\" This field is nested \"\"\"\n\n@dataclass\nclass Env:\n  nested_config: NestedEnv\n\n  my_number: int = 5\n  \"\"\" This is just a dummy number \"\"\"\n\n  my_string: str = \"Hello\"\n  \"\"\" A dummy string \"\"\"\n\n  my_flag: bool = False\n  \"\"\" Checkbox test \"\"\"\n\n  my_validated: Annotated[str, Validation(not_empty)] = \"hello\"\n  \"\"\" A validated field \"\"\"\n\nm = run(Env, title=\"My program\")\n# See some values\nprint(m.env.nested_config.another_number)  # 7\nprint(m.env)\n# Env(nested_config=NestedEnv(another_number=7), my_number=5, my_string='Hello', my_flag=False, my_validated='hello')\n\n# Edit values in a dialog\nm.form()\n
    "},{"location":"Mininterface/","title":"Mininterface","text":"

    Bases: Generic[EnvClass]

    The base interface. You get one through mininterface.run which fills CLI arguments and config file to mininterface.env or you can create one directly (without benefiting from the CLI parsing).

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.env","title":"env: EnvClass = _env or SimpleNamespace() instance-attribute","text":"

    Parsed arguments, fetched from cli Contains whole configuration (previously fetched from CLI and config file).

    $ program.py --number 10\n
    from dataclasses import dataclass\nfrom mininterface import run\n\n@dataclass\nclass Env:\n    number: int = 3\n    text: str = \"\"\n\nm = run(Env)\nprint(m.env.number)  # 10\n
    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.__enter__","title":"__enter__()","text":"

    When used in the with statement, the GUI window does not vanish between dialogs and it redirects the stdout to a text area.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.alert","title":"alert(text)","text":"

    Prompt the user to confirm the text.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.ask","title":"ask(text)","text":"

    Prompt the user to input a text.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.ask_number","title":"ask_number(text)","text":"

    Prompt the user to input a number. Empty input = 0.

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.form","title":"form(form=None, title='')","text":"

    Prompt the user to fill up whole form.

    Parameters:

    Name Type Description Default form FormDictOrEnv | None

    Dict of {labels: default value}. The form widget infers from the default value type. The dict can be nested, it can contain a subgroup. The default value might be mininterface.Tag that allows you to add descriptions. If None, the self.env is being used as a form, allowing the user to edit whole configuration. (Previously fetched from CLI and config file.) A checkbox example: {\"my label\": Tag(True, \"my description\")}

    None title str

    Optional form title

    ''"},{"location":"Mininterface/#mininterface.mininterface.Mininterface.is_yes","title":"is_yes(text)","text":"

    Display confirm box, focusing yes.

    m = run()\nprint(m.is_yes(\"Is that alright?\"))  # True/False\n

    "},{"location":"Mininterface/#mininterface.mininterface.Mininterface.is_no","title":"is_no(text)","text":"

    Display confirm box, focusing no.

    "},{"location":"Mininterface/#all-possible-interfaces","title":"All possible interfaces","text":"

    Several interfaces exist:

    • Mininterface \u2013 The base interface. Does not require any user input and hence is suitable for headless testing.
    • GuiInterface \u2013 A tkinter window.
    • TuiInterface \u2013 An interactive terminal.
    • TextualInterface \u2013 If textual installed, rich interface is used.
    • TextInterface \u2013 Plain text only interface with no dependency as a fallback.
    • ReplInterface \u2013 A debug terminal. Invokes a breakpoint after every dialog.

    Normally, you get an interface through mininterface.run but if you do not wish to parse CLI and config file, you can invoke one directly.

    with TuiInterface(\"My program\") as m:\n    number = m.ask_number(\"Returns number\")\n
    "},{"location":"Overview/","title":"Overview","text":""},{"location":"Overview/#basic-usage","title":"Basic usage","text":"

    Use a common dataclass, a Pydantic BaseModel or an attrs model to store the configuration. Wrap it to the run method that returns an interface m. Access the configuration via m.env or use it to prompt the user m.is_yes(\"Is that alright?\"). To do any advanced things, stick the value to a powerful Tag. For a validation only, use its Validation alias.

    "},{"location":"Overview/#supported-types","title":"Supported types","text":"

    Various types are supported: scalars, functions, well-known objects (Path), iterables, custom classes.

    Take a look how it works with the variables organized in a dataclass:

    from dataclasses import dataclass\nfrom pathlib import Path\n\nfrom mininterface import run\n\n\n@dataclass\nclass Env:\n    my_number: int = 1\n    \"\"\" A dummy number \"\"\"\n    my_boolean: bool = True\n    \"\"\" A dummy boolean \"\"\"\n    my_path: Path = Path(\"/tmp\")\n    \"\"\" A dummy path \"\"\"\n\n\nm = run(Env)  # m.env contains an Env instance\nm.form()  # Prompt a dialog; m.form() without parameter edits m.env\nprint(m.env)\n# Env(my_number=1, my_boolean=True, my_path=PosixPath('/tmp'),\n#  my_point=<__main__.Point object at 0x7ecb5427fdd0>)\n

    Variables organized in a dict:

    Along scalar types, there is (basic) support for common iterables or custom classes.

    from mininterface import run\n\nclass Point:\n    def __init__(self, i: int):\n        self.i = i\n\n    def __str__(self):\n        return str(self.i)\n\n\nvalues = {\"my_number\": 1,\n          \"my_list\": [1, 2, 3],\n          \"my_point\": Point(10)\n          }\n\nm = run()\nm.form(values)  # Prompt a dialog\nprint(values)  # {'my_number': 2, 'my_list': [2, 3], 'my_point': <__main__.Point object...>}\nprint(values[\"my_point\"].i)  # 100\n

    "},{"location":"Overview/#nested-configuration","title":"Nested configuration","text":"

    You can easily nest the configuration. (See also Tyro Hierarchical Configs).

    Just put another dataclass inside the config file:

    @dataclass\nclass FurtherConfig:\n    token: str\n    host: str = \"example.org\"\n\n@dataclass\nclass Config:\n    further: FurtherConfig\n\n...\nprint(config.further.host)  # example.org\n

    A subset might be defaulted in YAML:

    further:\n  host: example.com\n

    Or by CLI:

    $./program.py --further.host example.net\n
    "},{"location":"Standalone/","title":"Standalone","text":"

    When invoked directly, it creates simple GUI dialogs.

    $ mininterface  --help\nusage: Mininterface [-h] [OPTIONS]\n\nSimple GUI dialog. Outputs the value the user entered.\n\n\u256d\u2500 options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n\u2502 -h, --help              show this help message and exit                                   \u2502\n\u2502 --alert STR             Display the OK dialog with text. (default: '')                    \u2502\n\u2502 --ask STR               Prompt the user to input a text. (default: '')                    \u2502\n\u2502 --ask-number STR        Prompt the user to input a number. Empty input = 0. (default: '') \u2502\n\u2502 --is-yes STR            Display confirm box, focusing yes. (default: '')                  \u2502\n\u2502 --is-no STR             Display confirm box, focusing no. (default: '')                   \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n

    You can fetch a value to i.e. a bash script.

    $ mininterface  --ask-number \"What's your age?\"  # GUI window invoked\n18\n
    "},{"location":"Tag/","title":"Tag","text":"

    Wrapper around a value that encapsulates a description, validation etc. When you provide a value to an interface, you may instead use this object.

    Bridge between the input values and a UI widget. The widget is created with the help of this object, then transforms the value back (str to int conversion etc).

    "},{"location":"Tag/#mininterface.Tag.val","title":"val: TagValue = None","text":"

    The value wrapped by Tag.

    from mininterface import run, Tag\n\ntag = Tag(True, \"This is my boolean\", bool)\nm = run()\nm.form({\"My boolean\": tag})\nprint(tag.val)  # True/False\nprint()\n

    The encapsulated value is True, tag.description is 'This is my boolean', tag.annotation is bool and 'My boolean' is used as tag.name.

    "},{"location":"Tag/#mininterface.Tag.description","title":"description: str = ''","text":"

    The description displayed in the UI.

    "},{"location":"Tag/#mininterface.Tag.annotation","title":"annotation: type | None = None","text":"

    Used for validation (ex. to convert an empty string to None). If not set, will be determined automatically from the val type.

    "},{"location":"Tag/#mininterface.Tag.name","title":"name: str | None = None","text":"

    Name displayed in the UI.

    "},{"location":"Tag/#mininterface.Tag.validation","title":"validation: Callable[[Tag], ValidationResult | tuple[ValidationResult, TagValue]] | None = None","text":"

    When the user submits the form, the values are validated (and possibly transformed) with a callback function. If the validation fails, user is prompted to edit the value. Return True if validation succeeded or False or an error message when it failed.

    ValidationResult is a bool or the error message (that implicitly means it has failed).

    def check(tag: Tag):\n    if tag.val < 10:\n        return \"The value must be at least 10\"\nm.form({\"number\", Tag(12, validation=check)})\n

    Either use a custom callback function or mininterface.validators.

    from mininterface.validators import not_empty\nm.form({\"number\", Tag(\"\", validation=not_empty)})\n# User cannot leave the field empty.\n

    You may use the validation in a type annotation.

    from mininterface import Tag, Validation\n@dataclass\nclass Env:\n    my_text: Annotated[str, Validation(not_empty) = \"will not be emtpy\"\n\n    # which is an alias for:\n    # my_text: Annotated[str, Tag(validation=not_empty)] = \"will not be emtpy\"\n

    NOTE Undocumented feature, we can return tuple [ValidationResult, FieldValue] to set the self.val.

    "},{"location":"Tag/#mininterface.Tag.choices","title":"choices: list[str] | None = None","text":"

    TODO

    "},{"location":"Tag/#mininterface.Tag.original_val","title":"original_val = None","text":"

    Read only. The original value, preceding UI change. Handy while validating.

    def check(tag.val):\n    if tag.val != tag.original_val:\n        return \"You have to change the value.\"\nm.form({\"number\", Tag(8, validation=check)})\n
    "},{"location":"Tag/#mininterface.Tag.error_text","title":"error_text = None","text":"

    Read only. Error text if type check or validation fail and the UI has to be revised

    "},{"location":"Tag/#mininterface.Tag.update","title":"update","text":"

    UI value \u2192 Tag value \u2192 original value. (With type conversion and checks.)

    Parameters:

    Name Type Description Default ui_value TagValue

    The value as it has been updated in a UI. Update accordingly the value in the original linked dict/object the mininterface was invoked with.

    Validates the type and do the transformation. (Ex: Some values might be nulled from \"\".)

    required

    Returns:

    Type Description bool

    bool, whether the value is alright or whether the revision is needed.

    "},{"location":"Tag/#helper-types","title":"Helper types","text":""},{"location":"Tag/#mininterface.tag.ValidationResult","title":"ValidationResult = bool | ErrorMessage","text":"

    Callback validation result is either boolean or an error message.

    "},{"location":"Tag/#mininterface.tag.ErrorMessage","title":"ErrorMessage = TypeVar('ErrorMessage')","text":"

    A string, callback validation error message.

    "},{"location":"Tag/#mininterface.tag.TagValue","title":"TagValue = TypeVar('TagValue')","text":"

    Any value.

    "},{"location":"Validation/","title":"Validation","text":"

    We recommend to use the dataclass and validate with the Annotated keyword.

    from typing import Annotated\nfrom mininterface.validators import not_empty\nfrom mininterface import Validation\n\n@dataclass\nclass Env:\n    test: Annotated[str, Validation(not_empty)] = \"hello\"\n

    Under the hood, this is just a Tag.

    @dataclass\nclass Env:\n    test: Annotated[str, Tag(validation=not_empty)] = \"hello\"\n

    Why we used it in an Annotated statement? To preserve the date type.

    @dataclass\nclass Env:\n    my_string: Tag = Tag(\"hello\", validation=not_empty)\n\nm = run(Env)\nprint(type(m.env.my_string))  # Tag\nprint(m.env.my_string.val)  # hello\n
    "},{"location":"Validation/#validation-alias","title":"Validation alias","text":"

    Alias to Tag(validation=...)

    from mininterface import Tag, Validation\n@dataclass\nclass Env:\n    my_text: Annotated[str, Validation(not_empty) = \"will not be emtpy\"\n\n    # which is an alias for:\n    # my_text: Annotated[str, Tag(validation=not_empty)] = \"will not be emtpy\"\n

    Parameters:

    Name Type Description Default check Callable[[Tag], ValidationResult | tuple[ValidationResult, TagValue]]

    Callback function.

    required"},{"location":"Validation/#validators","title":"validators","text":"

    Functions suitable for Tag validation. When the user submits a value whose validation fails, they are prompted to edit the value.

    m = run()\nmy_dict = m.form({\"my_text\", Tag(\"\", validation=validators.not_empty)})\nmy_dict[\"my_text\"]  # You can be sure the value is not empty here.\n

    Note that alternatively to this module, you may validate with Pydantic or an attrs model.

    from pydantic import BaseModel, Field\n\nclass MyModel(BaseModel):\n    restrained: str = Field(default=\"hello\", max_length=5)\n
    import attr\nfrom attr.validators import max_len\n\n@attr.s\nclass AttrsModel:\n    restrained: str = attr.ib(default=\"hello\", validator=max_len(5))\n
    "},{"location":"Validation/#mininterface.validators.not_empty","title":"not_empty(tag)","text":"

    Assures that Tag the user has written a value and did not let the field empty.

    from mininterface import Tag, validators, run\n\nm = run()\nm.form({\"my_text\": Tag(\"\", validation=validators.not_empty)})\n# User cannot leave the string field empty.\n

    When submitting an empty value, a warning appears:

    Note that for Path, an empty string is converted to an empty Path('.'), hence '.' too is considered as an empty input and the user is not able to set '.' as a value. This does not seem to me as a bad behaviour as in CLI you clearly see the CWD, whereas in a UI the CWD is not evident.

    Parameters:

    Name Type Description Default tag Tag required"},{"location":"Validation/#mininterface.validators.limit","title":"limit(maxOrMin=None, max_=None, lt=None, gt=None, transform=False)","text":"

    Limit a number range or a string length.

    Either use as limit(maximum) or limit(minimum, maximum).

    Parameters:

    Name Type Description Default maximum int

    limit(maximum) \u2013 from zero (including) to maximum (including)

    required minimum int

    limit(minimum, maximum) \u2013 From minimum (including) to maximum (including)

    required lt float | None

    lesser than

    None gt float | None

    greater than

    None transform bool

    If the value is not withing the limit, transform it to a boundary.

    from mininterface import run, Tag\nfrom mininterface.validators import limit\n\nm = run()\nm.form({\"my_number\": Tag(2, validation=limit(1, 10, transform=True))})\n# Put there '50' \u2192 transformed to 10 and dialog reappears\n# with 'Value must be between 1 and 10.'\n

    False"},{"location":"run/","title":"Run","text":"

    The main access, start here. Wrap your configuration dataclass into run to access the interface. An interface is chosen automatically, with the preference of the graphical one, regressed to a text interface for machines without display. Besides, if given a configuration dataclass, the function enriches it with the CLI commands and possibly with the default from a config file if such exists. It searches the config file in the current working directory, with the program name ending on .yaml, ex: program.py will fetch ./program.yaml.

    Parameters:

    Name Type Description Default env_class Type[EnvClass] | None

    Dataclass with the configuration. Their values will be modified with the CLI arguments.

    None ask_on_empty_cli bool

    If program was launched with no arguments (empty CLI), invokes self.form() to edit the fields. (Withdrawn when ask_for_missing happens.)

    @dataclass\nclass Env:\nnumber: int = 3\ntext: str = \"\"\nm = run(Env, ask_on_empty=True)\n

    $ program.py  #  omitting all parameters\n# Dialog for `number` and `text` appears\n$ program.py --number 3\n# No dialog appears\n
    False title str

    The main title. If not set, taken from prog or program name.

    '' config_file Path | str | bool

    File to load YAML to be merged with the configuration. You do not have to re-define all the settings in the config file, you can choose a few. If set to True (default), we try to find one in the current working dir, whose name stem is the same as the program's. Ex: program.py will search for program.yaml. If False, no config file is used.

    True add_verbosity bool

    Adds the verbose flag that automatically sets the level to logging.INFO (-v) or logging.DEBUG (-vv).

    import logging\nlogger = logging.getLogger(__name__)\n\nm = run(Env, add_verbosity=True)\nlogger.info(\"Info shown\") # needs `-v` or `--verbose`\nlogger.debug(\"Debug not shown\")  # needs `-vv`\n# $ program.py --verbose\n# Info shown\n
    $ program.py --verbose\nInfo shown\n
    True ask_for_missing bool

    If some required fields are missing at startup, we ask for them in a UI instead of program exit.

    @dataclass\nclass Env:\nrequired_number: int\nm = run(Env, ask_for_missing=True)\n
    $ program.py  # omitting --required-number\n# Dialog for `required_number` appears\n
    True interface Type[Mininterface]

    Which interface to prefer. By default, we use the GUI, the fallback is the TUI. See the full list of possible interfaces.

    GuiInterface or TuiInterface Kwargs

    The same as for argparse.ArgumentParser.

    Returns:

    Type Description Mininterface[EnvClass]

    An interface, ready to be used.

    You cay context manager the function by a with statement. The stdout will be redirected to the interface (ex. a GUI window). TODO add wrap example

    Undocumented experimental: The env_class may be a function as well. We invoke its parameters. However, as Mininterface.env stores the output of the function instead of the Argparse namespace, methods like Mininterface.form(None) (to ask for editing the env values) will work unpredictibly. Also, the config file seems to be fetched only for positional (missing) parameters, and ignored for keyword (filled) parameters. It seems to be this is the tyro's deal and hence it might start working any time. If not, we might help it this way: if isinstance(config, FunctionType): config = lambda: config(**kwargs[\"default\"])

    Undocumented experimental: default keyword argument for tyro may serve for default values instead of a config file.

    "}]} \ No newline at end of file