Package: pypatconsole
PyPat-ConsoleA simple and sexy way to make an console interfaceHow to setupFirst install the package with the command (make sure you have Python 3.6 or higher)pip install pypatconsoleThen you can importpypatconsolein Python. The package lets you import three functions:menu,list_local_casesandclear_screen. Usage ofmenuwill be illustrated below.list_local_casestakes the output fromlocals()and lists all the functions in the local scope.clear_screenclears your screen, hence the name.How to implementSimply implement the cases (as functions) in a Python file, then to initialize the interface you simply use themenufunction at the bottomfrompypatconsoleimportmenu...menu(locals(),title=' Main menu title here ',main=True)You can import whatever modules, classes and functions you want in the file without them interfering with the functions defined your file. You will need to implement docstrings to every case. The first line of text in the docstring will be used as the description in the console interface.The order of the cases is alphabetically sorted by the function name.The function signature ofmenualong with its docstring is as follows:defmenu(cases:Union[List[Callable],Dict[str,Callable],ModuleType],title:str=strings.DEFAULT_TITLE,blank_proceedure:Union[str,Callable]="return",on_kbinterrupt:str="raise",decorator:Optional[Callable]=None,run:bool=True,main:bool=False,case_args:Optional[Dict[Callable,tuple]]=None,case_kwargs:Optional[Dict[Callable,dict]]=None,frontend:Optional[str]=None,):"""Factory function for the CLI class. This function initializes a menu.Parameters------------cases: Can be output of locals() (a dictionary) from the scope of the casesOr a list functionsOr a module containing the case functionstitle: title of menublank_proceedure: What to do the when given blank input. Can be user definedfunction, or it can be a string. Available string optionsare:'return', will return to parent menu'pass', does nothing. This should only be used for themain menudecorator: Decorator for case functionsrun: To invoke .run() method on CLI object or not.main: Tells the function whether or not the menu is the main menu (i.e. thefirst ("outermost") menu) or not. This basically sets the behavior on how the menushould behave. It is equivalent to give the argumnts on_kbinterrupt="return" andblank_proceedure="pass"cases_args: Optional[Dict[Callable, tuple]], dictionary with function as key and tuple ofpositional arguments as valuescases_kwargs: Optional[Dict[Callable, dict]], dictionary with function as key and dict ofkeyword arguments as valuesfrontend: str, specify desired frontend:"auto": Will try to use fancy frontend if curses module is available, elseuse simple frontend"fancy": Use fancy front end (if on Windows, installwindows-curses first or Python will not be able to find the required"curses" package that the fancy frontend uses)"simple": Use the simple (but compatible with basically everything) frontendReturns--------CLI (Command Line Interface) object. Use .run() method to activate menu."""ExamplesSay we are editing console.pyfromrandomimportrandintfromtimeimportsleepfrompypatconsoleimportmenudefcase1():'''FizzBuzz!When you get the urge to fizz your buzzif you know what I mean'''foriinrange(21):stringy=''fizz=i%3==0buzz=i%5==0iffizz:stringy=stringy+'Fizz'ifbuzz:stringy=stringy+'Buzz'ifnot(fizzorbuzz):stringy=iprint(stringy)sleep(0.1)defcase2():'''Print a small random integer'''print(randint(0,9))sleep(0.5)menu(locals(),title=' Main menu ',main=True,blank_proceedure='pass')will result with this when running:python console.py:-------------- Main menu ---------------
1. FizzBuzz!
2. Print a small random integer

Input:Special casesEntering..will exit the current menu, effectively moving you to the parent menu if you are implementing nested cases. If you are in the main menu it will exit the program.Enteringqwill raise a keyboard interrupt, menus can either return or raise KeyboardInterrupt in this case. See the docstring formenu.Enteringhwill display this text that explains the special cases.Enter-1or any integer will "reverse" the choices, such that you take the last choice. This is motivated by Python lists where you can index like list[-1]Frontend and usageThere are two frontends implemented; the simple frontend and the fancy frontend. The selection of frontend will be selected based on the detected operating system. One can pass the choice of frontend:menu(..., frontend="auto"). The possible choices areauto: Will try to use the fancy front end (usingcurses) by checking if thecursesmodule
is available, else use simple frontend.simple: Use simple frontend, should work on all systems since it is completely based on the
build in print function. Use by typing the corresponding key (e.g. 1) to the displayed cases and press enter.fancy: Use fancy frontend, will raiseImportErrorifcursesis unavailable. The fancy
frontend is "fancy" as in it gives visual indicators on what you are doing, and also adds
the the ability to traverse the options using the arrow keys.It possible to override the default frontend throughout the Python program by doingimportpypatconsoleasppcppc.config.default_frontend="auto"# auto, fancy, or simpleas opposed to specifying the choice of frontend for everymenu(..., frontend="...")call.ArgumentsThe cases can take arguments as well! Simply implement them as functions with type hints (type hints are mandatory for the case functions):frompypatconsoleimportmenudefcase1(a:int,b:int):'''Add two integers'''print(a+b)defcase1(a:str,b:str):'''Append two strings'''print(a+b)defcase3(a:list):'''Print elements in list and their types'''[print(f'Element{i}:{elem}, type:{type(elem)}')fori,eleminenumerate(a)]menu(locals(),title=' Main menu ',main=True)Then simply give the arguments along with the choice:-------------- Main menu ---------------
1. Add two integers
2. Append two strings
3. Print elements in list and their types


Input: 1 60 9

69Input: 2 "cat and dog" mathemathics

cat and dogmathemathics

Note: Single token strings don't even need quotesInput: 3 ['cat',69,420.0]

Element 0: cat, type: <class 'str'>
Element 1: 69, type: <class 'int'>
Element 2: 420.0, type: <class 'float'>

Note: You cannot have any spaces when giving lists, tuples, dicts and such as the input parser will break them.The program will read the desired types from the function signature, then it will convert the input into the appropriate types. The only supported types are the built in Python types:strintfloattuplelistsetdictHowever, if you need proper handling of argument types, I suggest you specify your inputs to be strings then manually handle them yourself to ensure expected behavior, as the process of turning the case inputs to
their designated types is done usingevalwith som string sanitation.Programmatic ArgumentsYou can supply arguments programmtically to your case functions:frompypatconsoleimportmenudefcase6(a,b,c,d):"""Programmatic arguments"""print(a,b,c,4)sleep(0.5)case_args={case6:(1,2)}case_kwargs={case6:{"d":4,"c":3}}menu(locals(),case_args=case_args,case_kwargs=case_kwargs)Case functions that takes arguments programmatically are not required to have type hints unlike case functions that are supposed take arguments through the cli.Functions that takes arguments programmatically cannot take arguments through the cli, that is you cannot both supply programmatic arguments as well as arguments through the cli.Nested casesIf you want to implement nested cases, then you can simply reuse the menu function in the function scope. When doing nested cases, you should not give the keywordmain=Trueto themenufunction.frompypatconsoleimportmenudefparentcase1():'''Fizz'''defsubcase1():'''docstring1'''passdefsubcase2():'''docstring2'''passmenu(locals(),title=' Title here ')menu(locals(),title=' Main menu ',main=True)You can create another module for the other cases and pass them as well:frompypatconsoleimportmenuimportother_casesdefsamplecase():'''Foo'''menu(other_cases,title=' Title here ')menu(other_cases,title=' Main menu ',main=True)or you can give a list of functions, which will enable you to force the ordering of the cases as well:frompypatconsoleimportmenudefparentcase1():'''Fizz'''defsubcase1():'''docstring1'''passdefsubcase2():'''docstring2'''passmenu([subcase2,subcase1],title=' Title here ')menu(locals(),title=' Main menu ')What if want to define functions without having them displayed in the menu?Of what I can think of: you can either define your functions in another python file and import that, or you can create a class (in the same file as the case functions) that consists of your functions as static methods.Optional: DecoratorTo enforce a common behavior when entering and leaving a case within a menu, you give a decorator to themenufunction. However, it is important that the decorator  implements the__wrapped__attribute (this is to handle docstrings of wrappers as arguments for wrapped functions). Generally, it should look like thisimportsleepfromfunctoolsimportwrapsdefcase_decorator(func):'''Decorator to enforce commmon behavior for cases'''@wraps(func)# VERY IMPORTANT TO WRAP FUNCTIONS TO ENSURE THAT# case_wrapper.__wrapped__ is set properlydefcase_wrapper(*args,**kwargs):'''Verbosity wrapper'''print('Yeah! Going in!')sleep(1)retobj=func(*args,**kwargs)print('Woah! Going out!')sleep(1)returnretobjreturncase_wrapperSince the decorator is a function, you cannot have it in the same namespace as the case functions, so you can for example implement it in another file. To use it you do as following:frompypatconsoleimportmenufromcase_decoratorimportcase_decorator# A lot of cases heremenu(locals(),decorator=case_decorator,main=True)WhySometimes you want to have a simple console interface so you can do things step by step.
Here are some applications:Stock data pipelineData scraping and data cleaning pipeline for stock data------------- Mulababy420 --------------
1. Update all data
2. Obtain Oslo Bors quotes and returns
3. Scrape Oslo bors HTML files
4. Scrape Yahoo Finance
5. Backup current data
6. Exit program
Enter choice:Sometimes I don't want to run everything at once. Maybe I just want to backup data instead of doing all everything. PyPat-Console will enable a very quick implementation of a console.
Without the console I would need to find the right file to run (and maybe comment things out first as well). The console organizes everything into one place.Database interface------BergenDB------
1. Log rent
2. Log power
3. Print table
4. Plot table
5. Commit changes
6. Discard changes
7. ExitI log my rent and power bills in a SQL database. I have made a Python API to manage the database, and I just do everything through the interface. No need to script anything or write any SQL queries.Control your Google Compute VM----------------GCE-----------------                                                                      
1. SSH to personal instance
2. SSH to project instance
3. start/stop personal instance (0 to stop, 1 to start, 2 to restart)
4. start/stop project instance (0 to stop, 1 to start, 2 to restart)
5. Get status personal instance
6. Get status project instance
7. Other instance control      

Entering blank returns to parent menu
Input: