menucmd 1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- menucmd-1.0/PKG-INFO +810 -0
- menucmd-1.0/README.md +798 -0
- menucmd-1.0/lib/__init__.py +0 -0
- menucmd-1.0/lib/menucmd.py +205 -0
- menucmd-1.0/macrolib/__init__.py +0 -0
- menucmd-1.0/macrolib/jsonmacros.py +21 -0
- menucmd-1.0/macrolib/typemacros.py +137 -0
- menucmd-1.0/menucmd.egg-info/PKG-INFO +810 -0
- menucmd-1.0/menucmd.egg-info/SOURCES.txt +13 -0
- menucmd-1.0/menucmd.egg-info/dependency_links.txt +1 -0
- menucmd-1.0/menucmd.egg-info/top_level.txt +3 -0
- menucmd-1.0/setup.cfg +4 -0
- menucmd-1.0/setup.py +21 -0
- menucmd-1.0/tests/__init__.py +0 -0
- menucmd-1.0/tests/menutesting.py +91 -0
menucmd-1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: menucmd
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Command line menu interface
|
|
5
|
+
Home-page: https://github.com/Casey-Litmer/menucmd
|
|
6
|
+
Author: Casey Litmer
|
|
7
|
+
Author-email: litmerc@msn.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.6
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
Getting Started
|
|
14
|
+
-
|
|
15
|
+
-----------------------------------------------------------------------------------------------------
|
|
16
|
+
MenuCMD is a library designed to easily create simple command line menus in a functional programming style.
|
|
17
|
+
The main goal is to separate functions from the way in which they are composed and how the user navigates them.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
This module can be used in several ways:
|
|
21
|
+
|
|
22
|
+
- as a debugging tool
|
|
23
|
+
- as a dedicated command line application interface
|
|
24
|
+
- as a framework for delayed function evaluation
|
|
25
|
+
|
|
26
|
+
Separating navigation from function definitions allows the user to later repurpose a program to run automatically
|
|
27
|
+
without relying on user input. What one creates as a command line interface one day, can easily be automated
|
|
28
|
+
the next.
|
|
29
|
+
|
|
30
|
+
Other features such as lazy evaluation with the `Bind` class can also be used independently from the menu
|
|
31
|
+
interface for your functional programming needs.
|
|
32
|
+
|
|
33
|
+
*For developers wishing to contribute to this module, a test script is included to ensure all
|
|
34
|
+
features work correctly between changes **(requires numpy)**. Feel free to update it as you see fit!*
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
## Sections:
|
|
38
|
+
### 1. Hello World
|
|
39
|
+
- Initializing a Menu
|
|
40
|
+
- Menu Item Format
|
|
41
|
+
- Writing Arguments
|
|
42
|
+
- Running 'Hello World'
|
|
43
|
+
### 2. Multiple Menus
|
|
44
|
+
- Defining Two Menus
|
|
45
|
+
- Exit Key Behaviour
|
|
46
|
+
- Adding More Entries
|
|
47
|
+
- End Chain Behaviour
|
|
48
|
+
- Serializing End Returns
|
|
49
|
+
### 3. Function Composition
|
|
50
|
+
- Using the Result Type
|
|
51
|
+
- Using Past Results
|
|
52
|
+
- Using the Bind Class
|
|
53
|
+
- Binding Functions and Kwargs
|
|
54
|
+
### 4. Other Menu Attributes
|
|
55
|
+
- Using Kwargs
|
|
56
|
+
- Using Manual Escapes
|
|
57
|
+
- Using Matching Keywords
|
|
58
|
+
### 5. Builtins
|
|
59
|
+
- In-line Functions
|
|
60
|
+
- Builtin Menus
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
1). Hello World
|
|
67
|
+
-
|
|
68
|
+
|
|
69
|
+
-----
|
|
70
|
+
### Initializing a Menu
|
|
71
|
+
|
|
72
|
+
Start by importing the `Menu` class.
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
from MenuCMD import Menu
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Create a new menu with a custom name.
|
|
79
|
+
This is what will appear at the top of the menu when run.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
#Create New Menu
|
|
83
|
+
menu1 = Menu(name = "First Menu")
|
|
84
|
+
```
|
|
85
|
+
-------
|
|
86
|
+
### Menu Item Format
|
|
87
|
+
You can add one or more items to a menu with the `append` method.
|
|
88
|
+
An item is a `tuple` composed of a key, a message, and another `tuple`
|
|
89
|
+
containing a chain of functions and arguments.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
("key", "message", (func1, (*args1), func2, (*args2),))
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
-----
|
|
96
|
+
### Writing Arguments
|
|
97
|
+
When the user selects an item, **func1** will run with **args1**, then **func2** will run with **args2**.
|
|
98
|
+
|
|
99
|
+
If a function only has one argument, you can simply write the pair as **(func1, arg1)**. \
|
|
100
|
+
**arg1** will automatically be wrapped as a `tuple`.
|
|
101
|
+
|
|
102
|
+
**If **arg1** is itself a tuple, write **(arg1,)**. Otherwise its contents will be interpreted as separate arguments!*
|
|
103
|
+
|
|
104
|
+
It is generally best practice to
|
|
105
|
+
keep the singleton tuple notation **(x,)** but is not strictly necessary.
|
|
106
|
+
|
|
107
|
+
-------
|
|
108
|
+
|
|
109
|
+
To add an item that prints 'hello world!' append the following:
|
|
110
|
+
```commandline
|
|
111
|
+
("x", "hello world program", (print, "Hello World!"))
|
|
112
|
+
```
|
|
113
|
+
This will display "hello world program" on the menu, and run the **print** function with argument "Hello World!"
|
|
114
|
+
when the user inputs "x".
|
|
115
|
+
|
|
116
|
+
To append item(s) to a menu:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
#Add an Item
|
|
120
|
+
menu1.append(
|
|
121
|
+
("x", "hello world program", (
|
|
122
|
+
print, "hello world!"
|
|
123
|
+
)),
|
|
124
|
+
item2,
|
|
125
|
+
item3,
|
|
126
|
+
...
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
*All menu items will appear in the order of appension.*
|
|
130
|
+
|
|
131
|
+
----
|
|
132
|
+
### Running 'Hello World'
|
|
133
|
+
Now all that's left is to run the menu by calling it with no arguments.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
from MenuCMD import Menu
|
|
137
|
+
|
|
138
|
+
#Create New Menu
|
|
139
|
+
menu1 = Menu(name = "First Menu")
|
|
140
|
+
|
|
141
|
+
#Add an Item
|
|
142
|
+
menu1.append(
|
|
143
|
+
("x", "hello world program", (
|
|
144
|
+
print, "hello world!"
|
|
145
|
+
))
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
#Run menu
|
|
149
|
+
menu1()
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The result should look like:
|
|
153
|
+
```commandline
|
|
154
|
+
First Menu
|
|
155
|
+
[x]- hello world program
|
|
156
|
+
[e]- exit
|
|
157
|
+
```
|
|
158
|
+
Inputing 'x' will print the desired text, returning to the menu:
|
|
159
|
+
```commandline
|
|
160
|
+
First Menu
|
|
161
|
+
[x]- hello world program
|
|
162
|
+
[e]- exit
|
|
163
|
+
x
|
|
164
|
+
hello world!
|
|
165
|
+
|
|
166
|
+
First Menu
|
|
167
|
+
[x]- hello world program
|
|
168
|
+
[e]- exit
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
In addition to the items you add, all menus will automatically add an exit key at the end of the list which
|
|
172
|
+
will break out of the menu by default. (This behaviour can be changed with the menu initialization)
|
|
173
|
+
|
|
174
|
+
When there is no more code to be run after the menu breaks, the program ends.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
2). Multiple Menus
|
|
178
|
+
-
|
|
179
|
+
----
|
|
180
|
+
### Defining Two Menus
|
|
181
|
+
Menus can open other menus by running them as functions allowing the user to navigate through a deeper menu structure.
|
|
182
|
+
|
|
183
|
+
First create a new `Menu` instance in the same way as **menu1**:
|
|
184
|
+
|
|
185
|
+
```commandline
|
|
186
|
+
#Create New Menus
|
|
187
|
+
menu1 = Menu(name = "First Menu")
|
|
188
|
+
menu2 = Menu(name = "Second Menu")
|
|
189
|
+
```
|
|
190
|
+
Then, add another entry to **menu1** that runs **menu2** with no arguments:
|
|
191
|
+
|
|
192
|
+
```commandline
|
|
193
|
+
menu1.append(
|
|
194
|
+
("x", "hello world program", (
|
|
195
|
+
print, "hello world!"
|
|
196
|
+
)),
|
|
197
|
+
("a", "menu2", (
|
|
198
|
+
menu2, ()
|
|
199
|
+
))
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
If we run the code and navigate to **menu2**, the following will happen:
|
|
204
|
+
|
|
205
|
+
```commandline
|
|
206
|
+
First Menu
|
|
207
|
+
[x]- hello world program
|
|
208
|
+
[a]- menu2
|
|
209
|
+
[e]- exit
|
|
210
|
+
a
|
|
211
|
+
--*No Entries*--
|
|
212
|
+
|
|
213
|
+
Process finished with exit code 0
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
We defined no entries for **menu2** so it automatically exits and, subsequently, the program,
|
|
217
|
+
as **menu1** has no more code to run.
|
|
218
|
+
|
|
219
|
+
*This results in the same outcome as pressing the exit key in **menu2**.*
|
|
220
|
+
|
|
221
|
+
-----------------------
|
|
222
|
+
### Exit Key Behaviour
|
|
223
|
+
The behaviour of the exit key can be changed with three keyword arguments
|
|
224
|
+
in a menu initiliazation:
|
|
225
|
+
```commandline
|
|
226
|
+
#exit_to : a function to be called on exit
|
|
227
|
+
#exit_key : the key to press (default 'e')
|
|
228
|
+
#exit_message : the message displayed on the menu (default 'exit')
|
|
229
|
+
```
|
|
230
|
+
----
|
|
231
|
+
To change the exit key behaviour to return to **menu1**, for example,
|
|
232
|
+
we can change the `exit_to` tag in the definition of **menu2** and the message it displays:
|
|
233
|
+
|
|
234
|
+
```commandline
|
|
235
|
+
menu1 = Menu(name = "First Menu")
|
|
236
|
+
menu2 = Menu(name = "Second Menu", exit_to = menu1, exit_message = "to menu1")
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
This will call **menu1** when the exit key is pressed or when **menu2** is empty.
|
|
240
|
+
|
|
241
|
+
*Additionally, you can change the `empty_message` keyword argument to change what message is displayed:*
|
|
242
|
+
|
|
243
|
+
```commandline
|
|
244
|
+
Menu(empty_message = "No more functions to run!")
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
------
|
|
248
|
+
### Adding More Entries
|
|
249
|
+
|
|
250
|
+
Let's add some entries to **menu2**:
|
|
251
|
+
|
|
252
|
+
```commandline
|
|
253
|
+
from MenuCMD import Menu
|
|
254
|
+
|
|
255
|
+
#Create New Menu
|
|
256
|
+
menu1 = Menu(name = "First Menu")
|
|
257
|
+
menu2 = Menu(name = "Second Menu", exit_to = menu1, exit_message = "to menu1")
|
|
258
|
+
|
|
259
|
+
#Add Items
|
|
260
|
+
menu1.append(
|
|
261
|
+
("x", "hello world program", (
|
|
262
|
+
print, "hello world!"
|
|
263
|
+
)),
|
|
264
|
+
("a", "menu2", (
|
|
265
|
+
menu2, ()
|
|
266
|
+
))
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
menu2.append(
|
|
270
|
+
("b", "happy b-day", (
|
|
271
|
+
print, "Happy Birthday!"
|
|
272
|
+
)),
|
|
273
|
+
("c", "merry christmas", (
|
|
274
|
+
print, "Merry Christmas!", menu1, ()
|
|
275
|
+
)),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
#Run Menu
|
|
279
|
+
menu1()
|
|
280
|
+
```
|
|
281
|
+
Pressing 'b' will print 'Happy Birthday!' and return to **menu2**. Pressing 'c'
|
|
282
|
+
will print 'Merry Christmas!' and return to **menu1**. This is because the second item runs the `print` function
|
|
283
|
+
first, and then calls **menu1**. The first item, however, does not call **menu1** so it returns to
|
|
284
|
+
**menu2** automatically.
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
### End Chain Behaviour
|
|
289
|
+
|
|
290
|
+
The default behaviour of a `None`-type return of the function chain can be changed
|
|
291
|
+
with the `end_to` keyword:
|
|
292
|
+
```commandline
|
|
293
|
+
#end_to : function to be called when the last function of a chain returns None
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
*By default, if the **last** function in a chain returns
|
|
297
|
+
`None`, the current menu will open **itself** after all functions are executed.*
|
|
298
|
+
|
|
299
|
+
----
|
|
300
|
+
### Serializing End Returns
|
|
301
|
+
|
|
302
|
+
The `print` function always returns `None`, so if you want all of the items in menu2 to return to menu1, you can
|
|
303
|
+
set
|
|
304
|
+
`end_to` = **menu1**, and neglect chaining **menu1** at the end of the entry.
|
|
305
|
+
|
|
306
|
+
```commandline
|
|
307
|
+
from MenuCMD import Menu
|
|
308
|
+
|
|
309
|
+
#Create New Menu
|
|
310
|
+
menu1 = Menu(name = "First Menu")
|
|
311
|
+
menu2 = Menu(name = "Second Menu",
|
|
312
|
+
exit_to = menu1, exit_message = "to menu1",
|
|
313
|
+
end_to = menu1
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
#Add Items
|
|
317
|
+
menu1.append(
|
|
318
|
+
("x", "hello world program", (
|
|
319
|
+
print, "hello world!"
|
|
320
|
+
)),
|
|
321
|
+
("a", "menu2", (
|
|
322
|
+
menu2, ()
|
|
323
|
+
))
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
menu2.append(
|
|
327
|
+
("b", "happy b-day", (
|
|
328
|
+
print, "happy birthday!"
|
|
329
|
+
)),
|
|
330
|
+
("c", "merry christmas", (
|
|
331
|
+
print, "merry christmas!"
|
|
332
|
+
)),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
#Run Menu
|
|
336
|
+
menu1()
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
This will not work for all functions, but for very simple menus, where the last function
|
|
340
|
+
always returns `None`, the `end_to` tag is a simple way to serialize menu behaviour.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
3). Function Composition
|
|
344
|
+
-
|
|
345
|
+
--------
|
|
346
|
+
### Using the Result Type
|
|
347
|
+
The `result` type allows the user to compose function returns in a chain.
|
|
348
|
+
Let's create a menu item that asks for a
|
|
349
|
+
number as input, squares it, and then prints the result.
|
|
350
|
+
|
|
351
|
+
For sake of terseness, redefine the namespace of `Menu.result` above the menu declarations.
|
|
352
|
+
```commandline
|
|
353
|
+
result = Menu.result
|
|
354
|
+
|
|
355
|
+
menu1 = Menu(name = "Function Composition")
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The first function in the chain asks the user for a number, the second function converts it to `float`, the third function
|
|
359
|
+
squares it, and the last function prints it.
|
|
360
|
+
|
|
361
|
+
`input -> float -> square -> print` => `print(square(float(input())))`
|
|
362
|
+
|
|
363
|
+
Use the `result` object for the argument of each consecutive function in the item:
|
|
364
|
+
|
|
365
|
+
```commandline
|
|
366
|
+
menu1.append(
|
|
367
|
+
("n", "square a number", (
|
|
368
|
+
input, "number ",
|
|
369
|
+
float, result,
|
|
370
|
+
lambda n: n**2, result,
|
|
371
|
+
print, result
|
|
372
|
+
)),
|
|
373
|
+
)
|
|
374
|
+
```
|
|
375
|
+
When the user selects an item, the result of each function is stored into the `result` object and
|
|
376
|
+
retrieved when calling the next function. A function may have `result` as any of its arguments as long as the chain composes types.
|
|
377
|
+
|
|
378
|
+
You can also substitute `result` for functions if the previous return type is a `function`.
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
#func1: *args1 -> function
|
|
382
|
+
|
|
383
|
+
(func1, (*args1), result, (*args2)) => (func1(*args1))(*args2)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
--------
|
|
387
|
+
### Using Past Results
|
|
388
|
+
|
|
389
|
+
The `result` object can also be indexed to retrieve all past results in a chain.
|
|
390
|
+
|
|
391
|
+
By default, it is indexed at -1, which is the previous return value.
|
|
392
|
+
```
|
|
393
|
+
result := result[-1]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
The indexing works exactly the same as a list.
|
|
397
|
+
|
|
398
|
+
If you want the first result in the chain, write `result[0]`.\
|
|
399
|
+
The result before the last; `result[-2]`.\
|
|
400
|
+
etc...
|
|
401
|
+
|
|
402
|
+
```commandline
|
|
403
|
+
# func1: () -> 1
|
|
404
|
+
# func2: x -> x+1
|
|
405
|
+
# func3: x -> x*2
|
|
406
|
+
# func4: x -> x**3
|
|
407
|
+
|
|
408
|
+
._________________________________________________.
|
|
409
|
+
.__________|_________.___________________. |
|
|
410
|
+
| | | | |
|
|
411
|
+
| | V V V
|
|
412
|
+
(func1, (), func2, (result[-1],), func3, (result[0],), func4, (result[-2],))
|
|
413
|
+
| | | |
|
|
414
|
+
V | | |
|
|
415
|
+
result[0] = 1 V | |
|
|
416
|
+
result[1] = 2 V |
|
|
417
|
+
result[2] = 2 V
|
|
418
|
+
result[3] = 8
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
For example, if we also wanted to print the pre-squared `float` value, we can add:
|
|
424
|
+
```commandline
|
|
425
|
+
#Create Menus
|
|
426
|
+
result = Menu.result
|
|
427
|
+
|
|
428
|
+
menu1 = Menu(name = "Function Composition")
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
#Append Items
|
|
432
|
+
menu1.append(
|
|
433
|
+
("n", "square a number", (
|
|
434
|
+
input, "number: ",
|
|
435
|
+
float, result,
|
|
436
|
+
lambda n: n**2, result,
|
|
437
|
+
print, result,
|
|
438
|
+
print, result[1]
|
|
439
|
+
) #result[1] prints the second result (= result[-3])
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
#Run
|
|
444
|
+
menu1()
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
-------
|
|
449
|
+
### Using the Bind Class
|
|
450
|
+
|
|
451
|
+
The `Bind` class allows lazy evaluation of functions. If the arguments defined in a menu item are not determined yet,
|
|
452
|
+
or a previous result must be converted before the menu substitutes its value, the `Bind` class will hold off evaluating a
|
|
453
|
+
function until it needs to.
|
|
454
|
+
|
|
455
|
+
Start by importing `Bind` into a convenient namespace:
|
|
456
|
+
```commandline
|
|
457
|
+
from MenuCMD import Menu, Bind as B
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
An object of the `Bind` class is essentially a *callable* function/argument(s) pair of the form
|
|
461
|
+
```commandline
|
|
462
|
+
B(func, *args)
|
|
463
|
+
```
|
|
464
|
+
The object takes a depth-first approach to evaluation so the deepest nested `Bind` object
|
|
465
|
+
will be evaluated first, and all the way up.
|
|
466
|
+
|
|
467
|
+
```commandline
|
|
468
|
+
B(func1, B(func2, B(func3, *args)))
|
|
469
|
+
|
|
470
|
+
#Evaluates to:
|
|
471
|
+
func1(func2(func3(*args)))
|
|
472
|
+
```
|
|
473
|
+
When a menu runs its function chains, it will evaluate all nested `Bind` objects as required.
|
|
474
|
+
|
|
475
|
+
*Outside of integrated menu usage, a `Bind` object can be called with no arguments and it will return its function
|
|
476
|
+
evaluated with its arguments.*
|
|
477
|
+
|
|
478
|
+
```commandline
|
|
479
|
+
lazy_func = B(func, *args)
|
|
480
|
+
|
|
481
|
+
lazy_func() -> func(*args)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
By default, if you don't use `Bind` in a menu item, and set the internal **args** to `result`, Python will attempt
|
|
485
|
+
to evaluate **func3(result)** before the item is even appended to the menu. But **result** *doesn't exist yet!*
|
|
486
|
+
|
|
487
|
+
A different way to appraoch the square number entry is to bind `float` with `result` and use it as the argument
|
|
488
|
+
to the squaring function:
|
|
489
|
+
|
|
490
|
+
```commandline
|
|
491
|
+
from MenuCMD import Menu, Bind as B
|
|
492
|
+
|
|
493
|
+
#Create New Menu
|
|
494
|
+
result = Menu.result
|
|
495
|
+
|
|
496
|
+
menu1 = Menu(name = "Function Composition")
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
#Add Items
|
|
500
|
+
menu1.append(
|
|
501
|
+
("n", "square a number", (
|
|
502
|
+
input, "number: ",
|
|
503
|
+
float, result,
|
|
504
|
+
lambda n: n**2, result,
|
|
505
|
+
print, result
|
|
506
|
+
)),
|
|
507
|
+
|
|
508
|
+
("m", "square a number (bind result)", (
|
|
509
|
+
input, "number: ",
|
|
510
|
+
lambda n: n**2, B(float, result),
|
|
511
|
+
print, result
|
|
512
|
+
))
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
#Run Menu
|
|
517
|
+
menu1()
|
|
518
|
+
```
|
|
519
|
+
This will effectively wait to evaluate the argument for lambda until `result` is known,
|
|
520
|
+
and then converts it to `float`.
|
|
521
|
+
|
|
522
|
+
------
|
|
523
|
+
### Binding Functions and Kwargs
|
|
524
|
+
|
|
525
|
+
Additionally, `Bind` objects can be nested in both the function and arguments.
|
|
526
|
+
|
|
527
|
+
For example, this is a completely valid `Bind` object:
|
|
528
|
+
```commandline
|
|
529
|
+
B(B(B(func1, *args1), *args2), B(func2, *args3))
|
|
530
|
+
|
|
531
|
+
#Evaluates to:
|
|
532
|
+
((func1(*args))(*args2))(func2(*args3))
|
|
533
|
+
```
|
|
534
|
+
If a **function** is undetermined, then it can also wait until the very last minute.
|
|
535
|
+
|
|
536
|
+
*Furthermore, `Bind` works exactly the same with keyword arguments.*
|
|
537
|
+
```commandline
|
|
538
|
+
B(func, *args, **kwargs)
|
|
539
|
+
```
|
|
540
|
+
---
|
|
541
|
+
4). Other Menu Attributes
|
|
542
|
+
-
|
|
543
|
+
-----
|
|
544
|
+
### Using kwargs
|
|
545
|
+
|
|
546
|
+
If a function also takes keyword arguments, use the `kwargs` wrapper from `Menu`. \
|
|
547
|
+
`kwargs` is simply a copy of `dict`. You may either wrap a set of keyword arguments
|
|
548
|
+
or input a dictionary:
|
|
549
|
+
|
|
550
|
+
```commandline
|
|
551
|
+
kwargs = Menu.kwargs
|
|
552
|
+
```
|
|
553
|
+
```
|
|
554
|
+
menu1.append(
|
|
555
|
+
("x", "function with kwargs", (
|
|
556
|
+
func, (*args, kwargs(kw1 = "a", kw2 = "b"))
|
|
557
|
+
))
|
|
558
|
+
)
|
|
559
|
+
```
|
|
560
|
+
Or
|
|
561
|
+
```
|
|
562
|
+
menu1.append(
|
|
563
|
+
("x", "function with kwargs", (
|
|
564
|
+
func, (*args, kwargs({"kw1":"a", "kw2":"b"})})
|
|
565
|
+
))
|
|
566
|
+
)
|
|
567
|
+
```
|
|
568
|
+
Both will evaluate to the same. If you do not wrap a dictionary with `kwargs`, it will be
|
|
569
|
+
interpreted as an argument.
|
|
570
|
+
|
|
571
|
+
----
|
|
572
|
+
|
|
573
|
+
### Using Manual Escapes
|
|
574
|
+
|
|
575
|
+
The `escape` type allows for manually breaking from a menu before a chain completes.
|
|
576
|
+
|
|
577
|
+
```commandline
|
|
578
|
+
escape = Menu.escape
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
If *any* function in the chain returns `escape`, no following functions will be executed and the
|
|
582
|
+
menu will instead run a different function.
|
|
583
|
+
|
|
584
|
+
By default, if an `escape` object is returned, the menu will return to itself but the behaviour
|
|
585
|
+
can be changed on menu initialization:
|
|
586
|
+
```commandline
|
|
587
|
+
# escape_to : a function to be called on manual escape
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
For example, define a function that returns `escape` if its input is empty:
|
|
591
|
+
```commandline
|
|
592
|
+
def check_if_empty(x):
|
|
593
|
+
if x:
|
|
594
|
+
return x
|
|
595
|
+
else:
|
|
596
|
+
return Menu.escape
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Then, say, if it isn't empty, print the string in reverse:
|
|
600
|
+
|
|
601
|
+
```commandline
|
|
602
|
+
menu2.append(
|
|
603
|
+
("x", "print if not empty", (
|
|
604
|
+
input, "Input a string ",
|
|
605
|
+
check_if_empty, result, #nothing will run after here if escape is returned
|
|
606
|
+
print, "reversed:",
|
|
607
|
+
print, B(lambda s: s[::-1], result[-2])
|
|
608
|
+
))
|
|
609
|
+
)
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
While this module is designed to allow complete independence of menu structures and functions,
|
|
613
|
+
the manual escape is the one exception to the rule. Although, in some cases, this can
|
|
614
|
+
be avoided with the builtin `escape_on` and `f_escape` functions covered in *Section 5)*.
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
### Using Matching Keywords
|
|
619
|
+
|
|
620
|
+
----
|
|
621
|
+
|
|
622
|
+
If a menu *exits*, *ends*, or *escapes* to the same function, you can optionally use matching
|
|
623
|
+
keywords to avoid writing the arguments multiple times.
|
|
624
|
+
|
|
625
|
+
For example, instead of writing
|
|
626
|
+
```commandline
|
|
627
|
+
menu1 = Menu()
|
|
628
|
+
|
|
629
|
+
menu2 = Menu(exit_to = menu1, end_to = menu1, escape_to = menu1)
|
|
630
|
+
|
|
631
|
+
menu3 = Menu(exit_to = menu2, end_to = menu1, escape_to = menu1)
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
you can use the `Menu.exit_to` and `Menu.end_to` keyword objects:
|
|
635
|
+
|
|
636
|
+
```commandline
|
|
637
|
+
menu1 = Menu()
|
|
638
|
+
|
|
639
|
+
menu2 = Menu(exit_to = menu1, end_to = Menu.exit_to, escape_to = Menu.exit_to)
|
|
640
|
+
|
|
641
|
+
menu3 = Menu(exit_to = menu2, end_to = menu1, escape_to = Menu.end_to)
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Setting any of the keyword arguments to `Menu.exit_to` will mirror the value of `exit_to`,
|
|
645
|
+
likewise for `Menu.end_to`.
|
|
646
|
+
|
|
647
|
+
The order of precedence for the `Menu.exit_to` and `Menu.end_to` objects is as follows:
|
|
648
|
+
```commandline
|
|
649
|
+
.______________.
|
|
650
|
+
._________._____|___________. |
|
|
651
|
+
| V | V V
|
|
652
|
+
exit_to | end_to | escape_to
|
|
653
|
+
| | | |
|
|
654
|
+
V | V |
|
|
655
|
+
Menu.exit_to Menu.end_to
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
`exit_to` will always be defined first, `end_to` second, and lastly `escape_to`. There is hence no
|
|
659
|
+
*Menu.escape_to* object.
|
|
660
|
+
|
|
661
|
+
Matching keywords serve as a nifty way to serialize `Menu` parameters.
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
5). Builtins
|
|
665
|
+
-
|
|
666
|
+
------
|
|
667
|
+
|
|
668
|
+
So far, this tutorial has approached creating menus as separate entities from the functions they compose.
|
|
669
|
+
While this is an intended feature of the module, you may still use menus within functions. MenuCMD has a number
|
|
670
|
+
of builtin functions to create template menus and to make in-line composition easier.
|
|
671
|
+
|
|
672
|
+
*This list is subject to change as community members can submit their own if anyone finds any common uses or improvements for MenuCMD!*
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
## In-line Functions
|
|
676
|
+
|
|
677
|
+
These functions are designed to be composed in menu item function chains to control command flow.
|
|
678
|
+
|
|
679
|
+
-----
|
|
680
|
+
|
|
681
|
+
### escape_on(x, value) -> value | Menu.escape
|
|
682
|
+
```
|
|
683
|
+
Returns an escape if the two arguments are equal, or both Truthy or both Falsy.
|
|
684
|
+
Otherwise, returns value.
|
|
685
|
+
```
|
|
686
|
+
Use this to break function chain execution on an equality condition. For example, to *escape* the menu on
|
|
687
|
+
empty input write:
|
|
688
|
+
```commandline
|
|
689
|
+
(input, "input", escape_on, ("", result), print, "your input:", print, result[-2])
|
|
690
|
+
```
|
|
691
|
+
This will bypass the print statements if the user inputs an empty string and calls whatever `escape_to`
|
|
692
|
+
is defined as.
|
|
693
|
+
|
|
694
|
+
-----
|
|
695
|
+
### f_escape(*args, **kwargs) -> Menu.escape
|
|
696
|
+
```
|
|
697
|
+
Polymorphic in-line escape function.
|
|
698
|
+
```
|
|
699
|
+
This function takes any **args** and **kwargs** and returns the `escape` object.
|
|
700
|
+
|
|
701
|
+
*For nerds, this can be viewed as the collection of terminal morphisms in **Hom(x, escape)***
|
|
702
|
+
|
|
703
|
+
----
|
|
704
|
+
### f_switch(n: int | bool, funcs: tuple[function]) -> Bind.Wrapper
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
```commandline
|
|
708
|
+
Returns a lazy function of type (int -> function)
|
|
709
|
+
```
|
|
710
|
+
Takes an `int` and a `tuple` of functions and returns a `Bind` that indexes each function.
|
|
711
|
+
|
|
712
|
+
Use this if you want a previous result to change which function runs next.
|
|
713
|
+
```commandline
|
|
714
|
+
functions = (func1, func2, func3)
|
|
715
|
+
|
|
716
|
+
(input, "choose a function (0-2) ", f_switch(result, functions), (*args))
|
|
717
|
+
#Call f_switch
|
|
718
|
+
```
|
|
719
|
+
Since `f_switch` returns a `Bind` object that subsequently returns a `function`, be sure to *call*
|
|
720
|
+
it in the function slot of the chain. Whatever function it switches to upon evaluating `result` (an integer),
|
|
721
|
+
will then be evaluated with **args**, so be sure that all of the functions in the `tuple` have the same domain!
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
-----
|
|
725
|
+
## Builtin Menus
|
|
726
|
+
|
|
727
|
+
These functions construct temporary menus that determine their returns. Optionally, all menu **kwargs**
|
|
728
|
+
can be passed through them to change the behaviour and appearance of the menus they invoke.
|
|
729
|
+
|
|
730
|
+
*Avoid changing the `exit_to` keyword argument as it will change the return type of the functions!*
|
|
731
|
+
|
|
732
|
+
------
|
|
733
|
+
|
|
734
|
+
### yesno_ver(**kwargs) -> bool
|
|
735
|
+
|
|
736
|
+
```commandline
|
|
737
|
+
Simple yes/no verification returning bool
|
|
738
|
+
```
|
|
739
|
+
Asks the user a yes/no question, and returns the bool of the result.
|
|
740
|
+
|
|
741
|
+
```commandline
|
|
742
|
+
yesno = yesno_ver()
|
|
743
|
+
|
|
744
|
+
print(yesno)
|
|
745
|
+
```
|
|
746
|
+
V V V V V V V
|
|
747
|
+
```commandline
|
|
748
|
+
Are you sure?
|
|
749
|
+
[x]- yes
|
|
750
|
+
[e]- cancel
|
|
751
|
+
x
|
|
752
|
+
True
|
|
753
|
+
```
|
|
754
|
+
```commandline
|
|
755
|
+
Are you sure?
|
|
756
|
+
[x]- yes
|
|
757
|
+
[e]- cancel
|
|
758
|
+
e
|
|
759
|
+
False
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
-----
|
|
763
|
+
### edit_list(entries: list | tuple, **kwargs) -> list | tuple
|
|
764
|
+
```commandline
|
|
765
|
+
Delete items in a list/tuple; returns updated list/tuple
|
|
766
|
+
```
|
|
767
|
+
Takes a `list` or `tuple` and displays a menu of numbered items to delete. Upon each selection, the menu
|
|
768
|
+
will display itself again with the item removed and will only return the updated `list`/`tuple` when the
|
|
769
|
+
exit key is pressed.
|
|
770
|
+
|
|
771
|
+
```commandline
|
|
772
|
+
L = ['a','b','c','d','e']
|
|
773
|
+
|
|
774
|
+
L = edit_list(L)
|
|
775
|
+
|
|
776
|
+
print(L)
|
|
777
|
+
```
|
|
778
|
+
V V V V V V V
|
|
779
|
+
```commandline
|
|
780
|
+
Edit List
|
|
781
|
+
[0]- a
|
|
782
|
+
[1]- b
|
|
783
|
+
[2]- c
|
|
784
|
+
[3]- d
|
|
785
|
+
[4]- e
|
|
786
|
+
[e]- exit
|
|
787
|
+
0
|
|
788
|
+
|
|
789
|
+
Edit List
|
|
790
|
+
[0]- b
|
|
791
|
+
[1]- c
|
|
792
|
+
[2]- d
|
|
793
|
+
[3]- e
|
|
794
|
+
[e]- exit
|
|
795
|
+
2
|
|
796
|
+
|
|
797
|
+
Edit List
|
|
798
|
+
[0]- b
|
|
799
|
+
[1]- c
|
|
800
|
+
[2]- e
|
|
801
|
+
[e]- exit
|
|
802
|
+
2
|
|
803
|
+
|
|
804
|
+
Edit List
|
|
805
|
+
[0]- b
|
|
806
|
+
[1]- c
|
|
807
|
+
[e]- exit
|
|
808
|
+
e
|
|
809
|
+
['b', 'c']
|
|
810
|
+
```
|