Package: asking
🙋 AskingAskingis a Python package for asking questions and getting answers.Getting startedHow does Asking work?InstallationQuick-start exampleScriptsScript structureAsking for a valueOffering the previous value as the defaultOffering multiple choice responsesOffering multiple choice responses with a defaultDynamic valuesMultiple stagesUsageLoadersStatesPerforming a scriptRunning non-interactively and unit testingActionsaskgotoresponsesstoptexttitleProjectContributingLicenceAuthorAcknowledgementsGetting startedHow does Asking work?In a nutshell, your Python script invokes Asking with two inputs:A script to follow.A dictionary to read default values out of and populate with new values.When the script ends, Asking returns some state to describe the success of the interaction, then the populated dictionary is yours to do with as you please.InstallationAsking requiresPython 3.8or later.Install Asking via pip:pipinstallaskingQuick-start exampleDownload thesample scriptto your working directory then runaskingto perform it:askingsample.asking.yamlNote that this will perform the script, but will not load or save any responses. Performing a script via the CLI application is intended only for testing.ScriptsScript structureAscriptis essentially a dictionary ofstages, and astageis a list ofactions.A script always starts with a stage named "start", and actions are invoked sequentially in order.Asking for a valuestart:-ask:question:Name?key:user.namebranches:-response:"^.+$"then:-stop:trueThis script asks the user for their name then exits.The script contains just one stage, named "start". The stage contains just one action: an "ask" action.The "ask" action contains a "question", which is printed to the screen. The "key" describes where in the dictionary the user's answer should be saved. "user.name" describes the "name" key of the "user" sub dictionary.The "branches" describe how to react to the user's answer. "response" is a regular expression. Asking checks the user's answer against each expression in order, and uses the first one that matches. In this case, there's only one choice. "then" describes the actions to invoke on that branch and, in this case, we "stop" and send truthiness back to the host application.For example:fromaskingimportdemoscript="""start:- ask:question: Name?key: user.namebranches:- response: "^.+$"then:- stop: true"""# demo() runs non-interactively with preset directions and# is intended for demonstrating only. See the usage# documentation for implementation guidance.demo(script,directions={"user":{"name":"Bobby Pringles"}})Name?
: Bobby Pringles

Stop reason: True
Responses:
{
  "user": {
    "name": "Bobby Pringles"
  }
}Offering the previous value as the defaultstart:-ask:question:Name?key:user.namerecall:truebranches:-response:"^.+$"then:-stop:trueThis example is identical to the previous except "recall" is now truthy. Asking will read the dictionary's current "key" value and offer it as the default value.For example:fromaskingimportdemoscript="""start:- ask:question: Name?key: user.namerecall: truebranches:- response: "^.+$"then:- stop: true"""# demo() runs non-interactively with preset directions and# is intended for demonstrating only. See the usage# documentation for implementation guidance.demo(script,responses={"user":{"name":"Bobby Sprinkles",},},directions={"user":{"name":"Bobby Pringles",},},)Name? (default: Bobby Sprinkles)
: Bobby Pringles

Stop reason: True
Responses:
{
  "user": {
    "name": "Bobby Pringles"
  }
}Offering multiple choice responsesstart:-ask:question:Cake?branches:-response:ythen:-stop:true-response:nthen:-stop:falseIn this example, the responses are plain strings rather than regular expressions. Asking will present all the available options.If the user enters "y" then Asking will stop and return truthiness to the host application. If the user enters "n" then Asking will stop and return falsiness.For example:fromaskingimportdemoscript="""start:- ask:question: Cake?key: cakebranches:- response: ythen:- stop: true- response: nthen:- stop: false"""# demo() runs non-interactively with preset directions and# is intended for demonstrating only. See the usage# documentation for implementation guidance.demo(script,directions={"cake":"n"})Cake? (y/n)
: n

Stop reason: False
Responses:
{
  "cake": "n"
}Offering multiple choice responses with a defaultstart:-ask:question:Cake?branches:-response:[y,""]then:-stop:true-response:nthen:-stop:falseThis is identical to the previous example, except now the first checked response is a list of options rather than a single string. Asking will use the first branch if the user hits enter without entering a value.For example:fromaskingimportdemoscript="""start:- ask:question: Cake?key: cakebranches:- response: [y, ""]then:- stop: true- response: nthen:- stop: false"""# demo() runs non-interactively with preset directions and# is intended for demonstrating only. See the usage# documentation for implementation guidance.demo(script,directions={"cake":""})Cake? (Y/n)
:

Stop reason: True
Responses:
{
  "cake": ""
}Dynamic valuesstart:-text:Today's date is {today}.-ask:question:Is this correct?branches:-response:ythen:-stop:true-response:nthen:-stop:falseTo set some text value at runtime, specify the key in braces and include the value in the state when performing the script:fromaskingimportask,State,YamlStringLoaderfromdatetimeimportdateloader=YamlStringLoader("""start:- text: Today's date is{today}.- ask:question: Is this correct?key: correctbranches:- response: ythen:- stop: true- response: nthen:- stop: false""")state=State({},color=False,directions={"correct":"y",},references={"today":date.today(),},)ask(loader,state)Today's date is 2021-11-30.

Is this correct? (y/n)
: yMultiple stagesstart:-ask:question:Cake?key:cakebranches:-response:[y,""]then:-goto:cake-response:nthen:-goto:teacake:-ask:question:Which cake?key:which_cakebranches:-response:"^.+$"then:-goto:teatea:-ask:question:Tea?key:teabranches:-response:[y,""]then:-goto:final-response:nthen:-goto:finalfinal:-text:Thank you for your order!-stop:trueThis example presents multiple questions. If the user wants cake then they're asked which one. If they don't want cake, or if they answered which cake they want, then they're asked if they want tea. Either way, the script ends with a thank-you message then stops successfully.For example:fromaskingimportdemoscript="""start:- ask:question: Cake?key: cakebranches:- response: [y, ""]then:- goto: cake- response: nthen:- goto: teacake:- ask:question: Which cake?key: which_cakebranches:- response: "^.+$"then:- goto: teatea:- ask:question: Tea?key: teabranches:- response: [y, ""]then:- goto: final- response: nthen:- goto: finalfinal:- text: Thank you for your order!- stop: true"""# demo() runs non-interactively with preset directions and# is intended for demonstrating only. See the usage# documentation for implementation guidance.demo(script,directions={"cake":"","which_cake":"Battenberg","tea":"",},)Cake? (Y/n)
:

Which cake?
: Battenberg

Tea? (Y/n)
:

Thank you for your order!

Stop reason: True
Responses:
{
  "cake": "",
  "tea": "",
  "which_cake": "Battenberg"
}UsageLoadersAsking offers the following options for loading a YAML script:DictionaryLoaderloads any Python dictionary.FileLoaderloads a YAML file.YamlResourceLoaderloads a YAML package resource.YamlStringLoaderloads a YAML string.StatesAStateinstance must be made available to your script. This describes the dictionary to read/write and any dynamic values.Performing a scriptEssentially, a script is performed by:Constructing a loader.Constructing a state, including your responses dictionary.Passing the loader and state intoask().fromaskingimportask,State,YamlResourceLoaderresponses={}loader=YamlResourceLoader(__package__,"sample.asking.yml")state=State(responses)stop_reason=ask(loader,state)Whenask()is complete, it will return whatever value the performance's "stop" action returned, and the responses dictionary will be populated with the user's answers.Running non-interactively and unit testingBy default, Asking will -- naturally -- ask users for input.To test your script without human interaction, you can pass directions into the state. This is a dictionary containing the value to respond with for each question key.For example:fromaskingimportask,State,FileLoaderfrompprintimportpprintresponses={}loader=FileLoader("sample.asking.yml")state=State(responses,color=False,directions={"ready":"y","user":{"name":"Bobby Pringles","smell":"Sulphur",},"save":"y",},)stop_reason=ask(loader,state)print("Stop reason:",stop_reason)pprint(responses)Asking Demo

Welcome to the Asking demo!

Are you ready? (y/N)
: y

Great! Let's get started!

What's your name?
: Bobby Pringles

What's your favourite smell?
: Sulphur

Here's your configuration:

{
  "ready": "y",
  "user": {
    "name": "Bobby Pringles",
    "smell": "Sulphur"
  }
}

Okay to save? (y/n)
: y

Stop reason: True
{'ready': 'y',
 'save': 'y',
 'user': {'name': 'Bobby Pringles', 'smell': 'Sulphur'}}These directions also allow you to invoke Asking in unit tests, and assert on stop reasons and dictionary values for given inputs.Actionsaskask:branches:-response:string or string list, requiredthen:-actionkey:stringquestion:string, requiredrecall:boolean, default=False"ask" asks a question.keydescribes the path in the responses dictionary to read/write the value. Use.as a path separator.recallindicates whether to load the response dictionary's current value as the default answer.branchesdescribes the possible reactions to the user's answer.responsecan be a string value, list of string values, or a regular expression. The first branch with a matching response will be followed.thendescribes the list of actions to perform when the branch is followed.gotogoto:stageWhen a "goto" action is encountered then execution will jump immediately to the specified stage.responsesresponses:jsonThe "responses" action prints the current values of the response dictionary.stopstop:anyThe "stop" action immediately stops the script and returns the reason to the host application.The reason is required but can be any value; even a list or dictionary.texttext:stringThe "text" action simply prints a line of text.titletitle:stringThe "title" action prints a line of text formatted as a title.ProjectContributingTo contribute a bug report, enhancement or feature request, please raise an issue atgithub.com/cariad/asking/issues.If you want to contribute a code change, please raise an issue first so we can chat about the direction you want to take.LicenceAsking is released atgithub.com/cariad/askingunder the MIT Licence.SeeLICENSEfor more information.AuthorHello! 👋 I'mCariad Ecclestonand I'm a freelance DevOps and backend engineer. My contact details are available on my personal wiki atcariad.earth.Please consider supporting my open source projects bysponsoring me on GitHub.Acknowledgements❤️ tojonathaneunice/ansiwrapfor neatly wrapping strings containing ANSI escape codes.Command line support byCline.YAML support byPyYAML.This documentation was pressed byEdition.