{ "info": { "author": "Konstantin Kovar", "author_email": "mail@vomkonstant.in", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development", "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: User Interfaces" ], "description": "# python-inquirer-executor\nThis is a wrapper around [python-inquirer](https://github.com/magmax/python-inquirer). From that project's README:\n\n> So, **Inquirer** should ease the process of asking end user **questions**, **parsing**, **validating** answers, managing **hierarchical prompts** and providing **error feedback**.\n\nThis package extends this thought by building classes on top of it to create prompts that will automatically call one or more functions corresponding to the user's choice, while keeping your code nice, tidy and readable. This is achieved by facilitating the docstring of your functions as user-facing representaion of these functions. \n\n## How to use\n### Installation\n\n```\npip install inquirer-executor\n```\n\nAs the whole code is contained in a single file and has only one dependecy (the built-upon [inquirer](https://github.com/magmax/python-inquirer) package), you can also manually copy/paste this (or parts of it) into your project, if that is how you roll.\n\n### Creating a single-choice question (List)\n```python\nfrom inquirer_executor import InquirerExecutorList \n\nquestion = InquirerExecutorList(\"Question you want to ask the user?\", list_of_functions)\n```\nThe `list_of_functions` can be any iterable that is exlusively composed of function types. The class will generate the question from the string you provided as the first argument and a list of functions that will be presented to the user as selectable options. These options will be represented by the given function's corresponding **docstring**. \n\n#### Example\n\n```python\nfrom inquirer_executor import InquirerExecutorList \n\ndef print_one():\n \"\"\"One.\"\"\"\n print(\"one\")\n\ndef print_two():\n \"\"\"Two.\"\"\"\n print(\"two\")\n\ndef print_three():\n \"\"\"Three.\"\"\"\n print(\"three\")\n\nquestion = InquirerExecutorList(\"Of the given choices, how many puppies is best?\", [print_one, print_two, print_three]) \n```\nThis will create the instance of the question. You now have `prompt_user()` and `prompt_and_execute()` methods at your disposal. Once you have used the `prompt_user()` method, and the user has provided an answer, you can also:\n- use the `find_function()` method to return the corresponding function to the user's answer\n- access the instances `answer` value to read the user's answer (the docstring they have selected *as* string)\n- use the `execute()` method to execute the users choice at a later point (the function returns the return value of the function called)\n\nFor now though, we are just going to use `prompt_and_execute()` to see the results right away:\n```python\nquestion.prompt_and_execute()\n```\nWhich gives us this output:\n```\n[?] Of the given choices, how many puppies is best?: Three.\n One.\n Two.\n > Three.\n\nthree\n```\nThe user has chosen from the docstrings representing the functions and the function got executed, printing 'three'. Neat.\n\n### Creating a multiple-choice question (Checkbox)\n```python\nfrom inquirer_executor import InquirerExecutorCheckbox\n\nquestion = InquirerExecutorCheckbox(\"Question you want to ask the user?\", list_of_functions)\n```\nInitializing this works exactly like it does for the `InquirerExecutorList` class. The difference is the existence of the `execution_stack` value, which is a list that contains all the options the user has checked. So this class will never return a single function, always a list of functions.\n\n\n#### Example\n```python\nfrom inquirer_executor import InquirerExecutorCheckbox\n\ndef print_puppies():\n \"\"\"Puppies.\"\"\"\n print(\"puppies\")\n\ndef print_rocks():\n \"\"\"Rocks.\"\"\"\n print(\"rocks\")\n\ndef print_kittens():\n \"\"\"Kittens.\"\"\"\n print(\"kittens\")\n\nquestion2 = InquirerExecutorCheckbox(\"Of the given choices, which ones are furry and cuddly?\", [print_puppies, print_rocks, print_kittens])\n```\nThis will create the instance of the question. Again, you now have `prompt_user()` and `prompt_and_execute()` methods at your disposal. Once you have generated an answer with the `prompt_user()` method, you can:\n- use the `find_functions()` *(mind the plural 's')* method to return the corresponding list of functions to the users answer\n- access the instances `answer` value to read the user's answers (a list of the docstrings they have selected)\n- use the `execute()` method to execute the users choices at a later point (the function itself returns a list of the functions return values)\n\nFor now though, we are again just going to use `prompt_and_execute()` to get this result:\n```\n[?] Of the given choices, which one's are furry and cuddly?: \n X Puppies.\n o Rocks.\n > X Kittens.\n\npuppies\nkittens\n```\nThe user has checked options one and three and the corresponing functions got called.\n\n*Keep in mind that in this case `prompt_and_execute()` always returns **a list**.*\n\n### Mutating the question after instantiation\n\n#### Adding\n\nThere are two ways to add functions to InquirerExecutor instances after they have been created. The first one is the `+` operator, that will append the added function to the end of the choices associated with the question.\n\nThe second one is the `insert(index, value)` method, that will insert a `value` (which in this case has to be a function type) at `index`. Use it like you are used to from the `list` type.\n\n#### Setting\n\nYou can also set new values as you are used to like \n```python\ninstance[0] = new_value\n```\nwhere again, `new_value` needs to be a function type.\n\n#### Reordering\n\nInquirerExecutor provides a `reorder(indices)` method where indices is a list of numbers that represent the new order, so when given `[2, 0, 1]`, the original index 0 would be moved to index 2, original index 1 moved to 0 and 2 to 1.\n\nYou can also use the `reverse()` method, which also works like you are used to from `list` types.\n\n#### Removing\n\nInquirerExecutor provides a `remove(value)` method, that excepts **either** a **function name** as string **or an index** as number as it's `value` argument. In both cases, the matching function is removed from the choices presented to the user.\n\n### Passing arguments\n\nYou can of course pass whatever arguments you like to your functions. Just keep in mind, that potentially any and every function in the list will be called, so all of your functions *must* accept the **same** parameters. To prevent possible errors down the road, InquirerExecuter **enforces this** at creation time and will throw an `AssertionError` if the accepted parameters of your functions don't match.\n\n### Theming\n\nYou can use [python-inquirer's built-in theming options](https://magmax.org/python-inquirer/usage.html#themes) with the key difference that you have to **instantiate** the theme **before using it**. You then pass the **instance** to the `prompt_user()` or `prompt_and_execute()` methods using the `theme` keyword, **not** the theme class.\n\n### Dynamically setting docstrings\n\nThis package makes it sometimes necessary - or at least preferable - to generate docstrings dynamically. This could be achieved by defining the docstring after you define the function like so:\n\n```python\nname = input(\"What is your name?\")\n\ndef some_function():\n \"\"\"Can't display the name variable here.\"\"\"\n return name\n\nsome_function.__doc__ = \"Returns your name: {}\".format(name)\n```\nThis is possible and valid as long as you are dealing with *normal* functions. As soon as you are trying to do this with methods inside a class, Python will raise an error telling you that the `__doc__` attribute of methods is not writable.\n\nFor this reason InquirerExecutor provides a decorator named `dynamic_docstring_decorator` that can be used to set dynamic docstrings. The above code rewritten with the decorator would look like this:\n\n```python\nfrom inquirer_executor import dynamic_docstring_decorator\n\nname = input(\"What is your name?\")\n\n@dynamic_docstring_decorator(\"Returns your name: {}\".format(name))\ndef some_function():\n \"\"\"Can't display the name variable here.\"\"\" # This docstring gets overwritten\n return name\n```\n\nMuch nicer and cleaner!\n\n### Using this as part of a whole catalogue of questions\n\nDepending on what you are trying to achieve you might want to organize the questions yourself in a manner that fits your use case best. For simple applications, InquirerExecutor provides a `QuestionsCatalogue` class, that can be instantiated with a n iterable type that consists of either `inquirer` or `inquirer_executor` objects. \n\nThe `QuestionsCatalogue` handles these objects so they feel just like a list of functions and equips you with it's `prompt_all()` method. This method returns a tuple of two items: 1) A dictionairy of all the answers given to the Text, Path, etc. prompts that you may have used directly from `inquirer` and 2) a list of functions the user has chosen from single- and multiple-choice questions in the `QuestionsCatalogue`. In order to keep everything human-readable and easy to reason about, this class provides no way of directly calling all functions, you need to call them yourself however and whenever you see fit.\n\n#### Example\n\n```python\nfrom inquirer import Text\nfrom inquirer_executor import (\n InquirerExecutorList as InqExList,\n InquirerExecutorCheckbox as InqExCheckbox,\n QuestionsCatalogue,\n)\n\ndef return_one():\n \"\"\"Return 1\"\"\"\n return 1\n\n\ndef return_two():\n \"\"\"Return 2\"\"\"\n return 2\n\n\ndef return_three():\n \"\"\"Return 3\"\"\"\n return 3\n\n\ndef return_four():\n \"\"\"Return 4\"\"\"\n return 4\n\n\ndef return_five():\n \"\"\"Return 5\"\"\"\n return 5\n\n\ninqex_checkbox = InqExCheckbox.from_iterable(\n \"What do you want to return?\", [return_one, return_two]\n)\n\ninqex_list = InqExList.from_iterable(\n \"What do you want to return?\", [return_three, return_four, return_five]\n)\n\ntext_question_first_name = Text(\"first_name\", message=\"What's your first name\")\n\ntext_question_last_name = Text(\"last_name\", message=\"What's your last name\")\n\nquestions_catalogue = QuestionsCatalogue(\n [inqex_checkbox, inqex_list, text_question_first_name, text_question_last_name]\n)\n\nprint(questions_catalogue.prompt_all())\n\n```\nAssuming the user checked both options at the checkbox and chose \"Return 4\" at the single choice question and he is a billionaire from Gotham City the above code would produce something like this:\n\n```\n[?] What do you want to return?: \n X Return 1\n > X Return 2\n\n[?] What do you want to return?: Return 4\n Return 3\n > Return 4\n Return 5\n\n[?] What's your first name: Bruce\n[?] What's your last name: Wayne\n({'first_name': 'Bruce', 'last_name': 'Wayne'}, [, , ])\n```\n## Examples\n\nIf you would like to see this package applied in a bit more complex examples, please do consult the [examples folder](https://github.com/Neugierdsnase/python-inquirer-executor/tree/master/examples) of the repository. These small projects are structured with human-readability in mind and are heavily commented to guide you through the code to get you working with this package in no time.\n\n## Raison D'\u00eatre\n\nI needed this myself.\n\n## Contributing\n\nContributions and improvements are very welcome. Please write a test for your code contribution and use the [Black code formatter](https://pypi.org/project/black/) when editing the code in this project.\n\nIf you have played around with the package and you think what you have created would make a good example project, I would absolutely love to merge it into the examples folder, please make sure to comment your code so others can understand what you are doing.\n\n## License\n\nCopyright (c) 2019 [Konstantin Kovar](https://blog.vomkonstant.in), based on [python-inquirer](https://github.com/magmax/python-inquirer), by Miguel \u00c1ngel Garc\u00eda ([@magmax9](https://twitter.com/magmax9)).\n\nLicensed under the [MIT license](https://github.com/Neugierdsnase/python-inquirer-executor/blob/master/LICENSE).\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Neugierdsnase/python-inquirer-executor", "keywords": "color terminal", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "inquirer-executor", "package_url": "https://pypi.org/project/inquirer-executor/", "platform": "", "project_url": "https://pypi.org/project/inquirer-executor/", "project_urls": { "Homepage": "https://github.com/Neugierdsnase/python-inquirer-executor" }, "release_url": "https://pypi.org/project/inquirer-executor/0.1.3/", "requires_dist": [ "inquirer (==2.6.3)" ], "requires_python": "", "summary": "A package to tightly bind user choices to functionality. Based on python-inquirer.", "version": "0.1.3" }, "last_serial": 5647038, "releases": { "0.1.3": [ { "comment_text": "", "digests": { "md5": "399aa1869077caf021f8da35fd4d05ca", "sha256": "b7dd0e859565287667a5d3eb8e3818ed3923d7236be2ce74fee6b08cfdb5cac3" }, "downloads": -1, "filename": "inquirer_executor-0.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "399aa1869077caf021f8da35fd4d05ca", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9699, "upload_time": "2019-08-07T21:16:33", "url": "https://files.pythonhosted.org/packages/6f/a6/79e8f9592be865078ab7e1f8669bd3cd41ccc551766627884517298f140a/inquirer_executor-0.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "37118353b434c8afb00199e39ab93cf7", "sha256": "0465d5d9664e86be22e80755ebc32725a3ba89d3e36dfd3b8db71eaa85acab41" }, "downloads": -1, "filename": "inquirer_executor-0.1.3.tar.gz", "has_sig": false, "md5_digest": "37118353b434c8afb00199e39ab93cf7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8987, "upload_time": "2019-08-07T21:16:36", "url": "https://files.pythonhosted.org/packages/d3/7f/9cf27f1f12cde9f4db1320525df0b0f7badd43a673bb8029da4bd6792954/inquirer_executor-0.1.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "399aa1869077caf021f8da35fd4d05ca", "sha256": "b7dd0e859565287667a5d3eb8e3818ed3923d7236be2ce74fee6b08cfdb5cac3" }, "downloads": -1, "filename": "inquirer_executor-0.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "399aa1869077caf021f8da35fd4d05ca", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9699, "upload_time": "2019-08-07T21:16:33", "url": "https://files.pythonhosted.org/packages/6f/a6/79e8f9592be865078ab7e1f8669bd3cd41ccc551766627884517298f140a/inquirer_executor-0.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "37118353b434c8afb00199e39ab93cf7", "sha256": "0465d5d9664e86be22e80755ebc32725a3ba89d3e36dfd3b8db71eaa85acab41" }, "downloads": -1, "filename": "inquirer_executor-0.1.3.tar.gz", "has_sig": false, "md5_digest": "37118353b434c8afb00199e39ab93cf7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8987, "upload_time": "2019-08-07T21:16:36", "url": "https://files.pythonhosted.org/packages/d3/7f/9cf27f1f12cde9f4db1320525df0b0f7badd43a673bb8029da4bd6792954/inquirer_executor-0.1.3.tar.gz" } ] }