You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/features/scripting.rst
+30-30Lines changed: 30 additions & 30 deletions
Original file line number
Diff line number
Diff line change
@@ -84,25 +84,25 @@ command. Here's a simple example that uses the arg_printer_ script::
84
84
system paths, and as shown above it has the ability to pass command-line
85
85
arguments to the scripts invoked.
86
86
87
-
Developing a CMD2 API
87
+
Developing a CMD2 API
88
88
---------------------
89
89
90
90
If you as an app designer have not explicitly disabled the run_pyscript command it must be assumed
91
91
that your application is structured for use in higher level python scripting. The following sections
92
92
are meant as guidelines and highlight possible pitfalls with both production and consumption
93
93
of API functionality. For clarity when speaking of "scripter" we are referring to those writing
94
-
scripts to be run by pyscript and "designer" as the CMD2 application author.
94
+
scripts to be run by pyscript and "designer" as the CMD2 application author.
95
95
96
96
Basics
97
97
~~~~~~
98
98
99
99
Without any work on the part of the designer, a scripter can take advantage of piecing together workflows
100
-
using simple ``app`` calls. The result of a run_pyscript app call yields a ``CommandResult`` object exposing
101
-
four members: ``Stdout``, ``Stderr``, ``Stop``, and ``Data``.
100
+
using simple ``app`` calls. The result of a run_pyscript app call yields a ``CommandResult`` object exposing
101
+
four members: ``Stdout``, ``Stderr``, ``Stop``, and ``Data``.
102
102
103
103
``Stdout`` and ``Stderr`` are fairly straightforward representations of normal data streams and accurately reflect
104
-
what is seen by the user during normal cmd2 interaction. ``Stop`` contains information about how the invoked
105
-
command has ended its lifecycle. Lastly ``Data`` contains any information the designer sets via ``self.last_result``
104
+
what is seen by the user during normal cmd2 interaction. ``Stop`` contains information about how the invoked
105
+
command has ended its lifecycle. Lastly ``Data`` contains any information the designer sets via ``self.last_result``
106
106
or ``self._cmd.last_result`` if called from inside a CommandSet.
107
107
108
108
@@ -118,7 +118,7 @@ where:
118
118
* ``command`` and ``args`` are entered exactly like they would be entered by
119
119
a user of your application.
120
120
121
-
Using fstrings tends to be the most straight forward and easily readable way to
121
+
Using fstrings tends to be the most straight forward and easily readable way to
122
122
provide parameters.::
123
123
124
124
first = 'first'
@@ -136,7 +136,7 @@ Design principles
136
136
If the cmd2 application follows the unix_design_philosophy_ a scriptor will have the most flexibility
137
137
to piece together workflows using different commands. If the designers' application is more complete
138
138
and less likely to be augmented in the future a scripter may opt for simple serial scripts with little
139
-
control flow. In either case, choices made by the designer will have effects on scripters.
139
+
control flow. In either case, choices made by the designer will have effects on scripters.
140
140
141
141
The following diagram illustrates the different boundaries to keep in mind.
142
142
@@ -167,7 +167,7 @@ The following diagram illustrates the different boundaries to keep in mind.
167
167
.. note::
168
168
169
169
As a designer it is preferable to design from the inside to out. Your code will be
170
-
infinitely far easier to unit test than at the higher level. While there are
170
+
infinitely far easier to unit test than at the higher level. While there are
171
171
regression testing extensions for cmd2 UnitTesting will always be faster for development.
172
172
173
173
.. warning::
@@ -178,7 +178,7 @@ The following diagram illustrates the different boundaries to keep in mind.
178
178
Developing a Basic API
179
179
~~~~~~~~~~~~~~~~~~~~~~
180
180
181
-
CMD2 out of the box allows scripters to take advantage of all exposed ``do_*`` commands. As a
181
+
CMD2 out of the box allows scripters to take advantage of all exposed ``do_*`` commands. As a
182
182
scripter one can easily interact with the application via ``stdout`` and ``stderr``.
183
183
184
184
As a baseline lets start off with the familiar FirstApp
@@ -243,7 +243,7 @@ Lets start off on the wrong foot::
243
243
^
244
244
SyntaxError: unexpected EOF while parsing
245
245
246
-
cmd2 pyscripts require **valid** python code as a first step.
246
+
cmd2 pyscripts require **valid** python code as a first step.
247
247
248
248
.. warning::
249
249
@@ -253,7 +253,7 @@ cmd2 pyscripts require **valid** python code as a first step.
253
253
254
254
When executing the ``speak`` command without parameters you see the following error::
255
255
256
-
(Cmd) speak
256
+
(Cmd) speak
257
257
Usage: speak [-h] [-p] [-s] [-r REPEAT] words [...]
258
258
Error: the following arguments are required: words
259
259
@@ -264,19 +264,19 @@ Even though this is a fully qualified CMD2 error the py_script must look for thi
264
264
265
265
::
266
266
267
-
(Cmd) run_pyscript script.py
267
+
(Cmd) run_pyscript script.py
268
268
Working
269
-
(Cmd)
269
+
(Cmd)
270
270
271
-
You should notice that no error message is printed. Let's utilize the ``CommandResult``
271
+
You should notice that no error message is printed. Let's utilize the ``CommandResult``
272
272
object to inspect the actual returned data.::
273
273
274
274
result = app('speak')
275
275
print(result)
276
276
277
277
::
278
278
279
-
(Cmd) run_pyscript script.py
279
+
(Cmd) run_pyscript script.py
280
280
CommandResult(stdout='', stderr='Usage: speak [-h] [-p] [-s] [-r REPEAT] words [...]\nError: the following arguments are required: words\n\n', stop=False, data=None)
281
281
282
282
Now we can see that there has been an error. Let's re write the script to perform error checking.::
@@ -288,7 +288,7 @@ Now we can see that there has been an error. Let's re write the script to perfor
288
288
289
289
::
290
290
291
-
(Cmd) run_pyscript script.py
291
+
(Cmd) run_pyscript script.py
292
292
Something went wrong
293
293
294
294
In python development is good practice to fail and exit quickly after user input.::
@@ -305,10 +305,10 @@ In python development is good practice to fail and exit quickly after user input
305
305
306
306
::
307
307
308
-
(Cmd) run_pyscript script.py
308
+
(Cmd) run_pyscript script.py
309
309
Continuing along..
310
310
311
-
We changed the input to be a valid ``speak`` command but no output. Again we must inspect the
311
+
We changed the input to be a valid ``speak`` command but no output. Again we must inspect the
312
312
``CommandResult``::
313
313
314
314
import sys
@@ -323,7 +323,7 @@ We changed the input to be a valid ``speak`` command but no output. Again we mus
323
323
324
324
::
325
325
326
-
(Cmd) run_pyscript script.py
326
+
(Cmd) run_pyscript script.py
327
327
TRUTH!!!
328
328
329
329
By just using ``stdout`` and ``stderr`` it is possible to string together commands
@@ -334,7 +334,7 @@ Developing an Advanced API
334
334
~~~~~~~~~~~~~~~~~~~~~~~~~~
335
335
336
336
Until now the application designer has paid little attention to scripters and their needs.
337
-
Wouldn't it be nice if while creating py_scripts one did not have to parse data from ``stdout``? We can
337
+
Wouldn't it be nice if while creating py_scripts one did not have to parse data from ``stdout``? We can
338
338
accomodate the weary scripter by adding one small line at the end of our ``do_*`` commands.
339
339
340
340
``self.last_result = <value>``
@@ -378,22 +378,22 @@ The following script retrieves the array contents.::
378
378
379
379
Results::
380
380
381
-
Cmd) run_pyscript script.py
381
+
Cmd) run_pyscript script.py
382
382
['.venv', 'app.py', 'script.py']
383
383
384
-
As a rule of thumb it is safer for the designer to return simple scalar types as command results instead of complex objects.
385
-
If there is benefit in providing class objects designers should choose immutable over mutable types and never
384
+
As a rule of thumb it is safer for the designer to return simple scalar types as command results instead of complex
385
+
objects. If there is benefit in providing class objects designers should choose immutable over mutable types and never
386
386
provide direct access to class members as this could potentially lead to violation of the open_closed_principle_.
387
387
388
-
When possible, a dataclass is a lightweight solution perfectly suited for data manipulation. Lets dive into an
388
+
When possible, a dataclass is a lightweight solution perfectly suited for data manipulation. Lets dive into an
389
389
example.
390
390
391
391
The following fictitional application has two commands: ``build`` and ``status``. We can pretend that the build action
392
392
happens somewhere else in the world at an REST API endpoint and has significant computational cost. The status command
393
393
for all intents and purposes will only show the current status of a build task. The application has provided all that is
394
-
needed for a user to start a build and then determine it's status. The problem however is that with a long running process
395
-
the user may want to wait for it to finish. A designer may be tempted to create a command to start a build and then
396
-
poll for status until finished but this scenario is better solved as an extensible script.
394
+
needed for a user to start a build and then determine it's status. The problem however is that with a long running
395
+
process the user may want to wait for it to finish. A designer may be tempted to create a command to start a build and
396
+
then poll for status until finished but this scenario is better solved as an extensible script.
397
397
398
398
app.py::
399
399
@@ -501,7 +501,7 @@ The below is a possible solution via pyscript::
501
501
502
502
build_status = result.data
503
503
504
-
# If the status shows complete then we are done
504
+
# If the status shows complete then we are done
505
505
if build_status.status in ['finished', 'canceled']:
506
506
print(f"Build {build.name} has completed")
507
507
break
@@ -520,4 +520,4 @@ The below is a possible solution via pyscript::
0 commit comments