Skip to content

Commit bebad50

Browse files
committed
➕ Adds some more in depth descriptions to what's happening between steps
1 parent 8a4131a commit bebad50

File tree

1 file changed

+195
-64
lines changed

1 file changed

+195
-64
lines changed

ch04packaging/02Argparse.ipynb

Lines changed: 195 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,74 @@
1414
"This is the standard library for building programs with a command-line interface. Here we show a short introduction to it, but we recommend to read the [official tutorial](https://docs.python.org/3/howto/argparse.html)."
1515
]
1616
},
17+
{
18+
"cell_type": "markdown",
19+
"metadata": {},
20+
"source": [
21+
"Let's start by creating a simple `greet` function that accepts some parameters."
22+
]
23+
},
1724
{
1825
"cell_type": "code",
19-
"execution_count": 7,
20-
"metadata": {
21-
"collapsed": false
22-
},
26+
"execution_count": 1,
27+
"metadata": {},
28+
"outputs": [],
29+
"source": [
30+
"def greet(personal, family, title=\"\", polite=False):\n",
31+
" greeting = \"How do you do, \" if polite else \"Hey, \"\n",
32+
" if title:\n",
33+
" greeting += f\"{title} \"\n",
34+
"\n",
35+
" greeting += f\"{personal} {family}.\"\n",
36+
" return greeting"
37+
]
38+
},
39+
{
40+
"cell_type": "markdown",
41+
"metadata": {},
42+
"source": [
43+
"Now we have a function that greets whoever we want."
44+
]
45+
},
46+
{
47+
"cell_type": "code",
48+
"execution_count": 2,
49+
"metadata": {},
50+
"outputs": [
51+
{
52+
"data": {
53+
"text/plain": [
54+
"'How do you do, John Cleese.'"
55+
]
56+
},
57+
"execution_count": 2,
58+
"metadata": {},
59+
"output_type": "execute_result"
60+
}
61+
],
62+
"source": [
63+
"greet(\"John\", \"Cleese\", polite=True)"
64+
]
65+
},
66+
{
67+
"cell_type": "markdown",
68+
"metadata": {},
69+
"source": [
70+
"If we want to create a command line interface for this function, we need to save it on its own file. To add the capability to accept inputs from the command line we are going to use `argparse`.\n",
71+
"\n",
72+
"Rememer, what's under the `if __name__ == \"__main__\":` block is what's get executed when you run the file!"
73+
]
74+
},
75+
{
76+
"cell_type": "code",
77+
"execution_count": 3,
78+
"metadata": {},
2379
"outputs": [
2480
{
25-
"output_type": "stream",
2681
"name": "stdout",
82+
"output_type": "stream",
2783
"text": [
28-
"Overwriting greeter.py\n"
84+
"Writing greeter.py\n"
2985
]
3086
}
3187
],
@@ -34,41 +90,45 @@
3490
"#!/usr/bin/env python\n",
3591
"from argparse import ArgumentParser\n",
3692
"\n",
37-
"def parse_arguments(): \n",
93+
"def greet(personal, family, title=\"\", polite=False):\n",
94+
" greeting = \"How do you do, \" if polite else \"Hey, \"\n",
95+
" if title:\n",
96+
" greeting += f\"{title} \"\n",
97+
"\n",
98+
" greeting += f\"{personal} {family}.\"\n",
99+
" return greeting\n",
100+
"\n",
101+
"if __name__ == \"__main__\":\n",
38102
" parser = ArgumentParser(description=\"Generate appropriate greetings\")\n",
39103
" parser.add_argument('--title', '-t')\n",
40104
" parser.add_argument('--polite','-p', action=\"store_true\")\n",
41105
" parser.add_argument('personal')\n",
42106
" parser.add_argument('family')\n",
43107
" arguments= parser.parse_args()\n",
44-
" return arguments\n",
45-
"\n",
46-
"def greet(arguments):\n",
47-
" greeting = \"How do you do, \" if arguments.polite else \"Hey, \"\n",
48-
" if arguments.title:\n",
49-
" greeting += f\"{arguments.title} \"\n",
50-
" greeting += f\"{arguments.personal} {arguments.family}.\"\n",
51-
" print(greeting)\n",
52-
"\n",
53-
"\n",
54-
"if __name__ == \"__main__\":\n",
55-
" arguments = parse_arguments()\n",
56-
" greet(arguments)"
108+
" \n",
109+
" message = greet(arguments.personal, arguments.family,\n",
110+
" arguments.title, arguments.polite)\n",
111+
" print(message)"
57112
]
58113
},
59114
{
60115
"cell_type": "markdown",
61116
"metadata": {},
62117
"source": [
63-
"If you are using MacOS or Linux, you do the following to create an executable:"
118+
"Note that we've created arguments for each argument `greet` accepts and kept what's optional in the function (the keyword arguments) to be also optional for its command-line interface (can you spot how?)."
119+
]
120+
},
121+
{
122+
"cell_type": "markdown",
123+
"metadata": {},
124+
"source": [
125+
"We need to tell the computer that this file can be executed to be able to run this script without calling it with `python` everytime. The computer will know what to use by reading the [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) `#!`. If you are using MacOS or Linux, you do the following to create an executable:"
64126
]
65127
},
66128
{
67129
"cell_type": "code",
68-
"execution_count": 3,
69-
"metadata": {
70-
"collapsed": true
71-
},
130+
"execution_count": 4,
131+
"metadata": {},
72132
"outputs": [],
73133
"source": [
74134
"%%bash\n",
@@ -84,45 +144,29 @@
84144
},
85145
{
86146
"cell_type": "code",
87-
"execution_count": 10,
88-
"metadata": {
89-
"collapsed": false
90-
},
147+
"execution_count": 5,
148+
"metadata": {},
91149
"outputs": [
92150
{
93-
"output_type": "stream",
94151
"name": "stderr",
152+
"output_type": "stream",
95153
"text": [
96-
"': No such file or directory\n"
97-
]
98-
},
99-
{
100-
"output_type": "error",
101-
"ename": "CalledProcessError",
102-
"evalue": "Command 'b'./greeter.py --help\\n'' returned non-zero exit status 127.",
103-
"traceback": [
104-
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
105-
"\u001b[1;31mCalledProcessError\u001b[0m Traceback (most recent call last)",
106-
"\u001b[1;32m<ipython-input-10-1ccc774e4e21>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mget_ipython\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrun_cell_magic\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'bash'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m''\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'./greeter.py --help\\n'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
107-
"\u001b[1;32m~\\anaconda3\\lib\\site-packages\\IPython\\core\\interactiveshell.py\u001b[0m in \u001b[0;36mrun_cell_magic\u001b[1;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[0;32m 2369\u001b[0m \u001b[1;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbuiltin_trap\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2370\u001b[0m \u001b[0margs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mmagic_arg_s\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 2371\u001b[1;33m \u001b[0mresult\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2372\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2373\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
108-
"\u001b[1;32m~\\anaconda3\\lib\\site-packages\\IPython\\core\\magics\\script.py\u001b[0m in \u001b[0;36mnamed_script_magic\u001b[1;34m(line, cell)\u001b[0m\n\u001b[0;32m 140\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 141\u001b[0m \u001b[0mline\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mscript\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 142\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mshebang\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mline\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 143\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 144\u001b[0m \u001b[1;31m# write a basic docstring:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
109-
"\u001b[1;32m<decorator-gen-111>\u001b[0m in \u001b[0;36mshebang\u001b[1;34m(self, line, cell)\u001b[0m\n",
110-
"\u001b[1;32m~\\anaconda3\\lib\\site-packages\\IPython\\core\\magic.py\u001b[0m in \u001b[0;36m<lambda>\u001b[1;34m(f, *a, **k)\u001b[0m\n\u001b[0;32m 185\u001b[0m \u001b[1;31m# but it's overkill for just that one bit of state.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 186\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mmagic_deco\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 187\u001b[1;33m \u001b[0mcall\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mlambda\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mk\u001b[0m\u001b[1;33m:\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mk\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 188\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 189\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0marg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
111-
"\u001b[1;32m~\\anaconda3\\lib\\site-packages\\IPython\\core\\magics\\script.py\u001b[0m in \u001b[0;36mshebang\u001b[1;34m(self, line, cell)\u001b[0m\n\u001b[0;32m 243\u001b[0m \u001b[0msys\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstderr\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 244\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mraise_error\u001b[0m \u001b[1;32mand\u001b[0m \u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreturncode\u001b[0m\u001b[1;33m!=\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 245\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mCalledProcessError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreturncode\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moutput\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mout\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstderr\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0merr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 246\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 247\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0m_run_script\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mp\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mto_close\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
112-
"\u001b[1;31mCalledProcessError\u001b[0m: Command 'b'./greeter.py --help\\n'' returned non-zero exit status 127."
154+
"usage: greeter.py [-h] [--title TITLE] [--polite] personal family\n",
155+
"greeter.py: error: the following arguments are required: personal, family\n"
113156
]
114157
}
115158
],
116159
"source": [
117-
"%%bash\n",
118-
"./greeter.py --help"
160+
"%%bash --no-raise-error\n",
161+
"./greeter.py"
119162
]
120163
},
121164
{
122165
"cell_type": "markdown",
123166
"metadata": {},
124167
"source": [
125-
"if you are using Windows, change `bash` by `cmd`, and prepend the commands by `python`\n",
168+
"if you are using Windows' commands or powershell terminal (instead of git-bash), then the shebang is ignored and you will have to call `python` explicitily. Additionally, for the notebooks cells, you need to change `bash` by `cmd`.\n",
169+
"\n",
126170
"```\n",
127171
"%%cmd\n",
128172
"python greeter.py John Cleese\n",
@@ -131,10 +175,8 @@
131175
},
132176
{
133177
"cell_type": "code",
134-
"execution_count": 4,
135-
"metadata": {
136-
"collapsed": false
137-
},
178+
"execution_count": 6,
179+
"metadata": {},
138180
"outputs": [
139181
{
140182
"name": "stdout",
@@ -149,12 +191,17 @@
149191
"./greeter.py John Cleese"
150192
]
151193
},
194+
{
195+
"cell_type": "markdown",
196+
"metadata": {},
197+
"source": [
198+
"We can then use the optional arguments as:"
199+
]
200+
},
152201
{
153202
"cell_type": "code",
154-
"execution_count": 5,
155-
"metadata": {
156-
"collapsed": false
157-
},
203+
"execution_count": 7,
204+
"metadata": {},
158205
"outputs": [
159206
{
160207
"name": "stdout",
@@ -171,10 +218,8 @@
171218
},
172219
{
173220
"cell_type": "code",
174-
"execution_count": 6,
175-
"metadata": {
176-
"collapsed": false
177-
},
221+
"execution_count": 8,
222+
"metadata": {},
178223
"outputs": [
179224
{
180225
"name": "stdout",
@@ -195,6 +240,92 @@
195240
"source": [
196241
"Yes, [he is](https://en.wikipedia.org/wiki/John_Cleese#Honours_and_tributes)!"
197242
]
243+
},
244+
{
245+
"cell_type": "markdown",
246+
"metadata": {},
247+
"source": [
248+
"From the error we got above when we called `greeter.py` without arguments, you may have noticed that in the usage message there's also a `-h` optional argument. We know it's optional because it's shown within square brackes, like for `[--polite]`. This new argument, as the usage message seen above, is generated automatically by argparse and you can use it to see the help."
249+
]
250+
},
251+
{
252+
"cell_type": "code",
253+
"execution_count": 9,
254+
"metadata": {},
255+
"outputs": [
256+
{
257+
"name": "stdout",
258+
"output_type": "stream",
259+
"text": [
260+
"usage: greeter.py [-h] [--title TITLE] [--polite] personal family\n",
261+
"\n",
262+
"Generate appropriate greetings\n",
263+
"\n",
264+
"positional arguments:\n",
265+
" personal\n",
266+
" family\n",
267+
"\n",
268+
"optional arguments:\n",
269+
" -h, --help show this help message and exit\n",
270+
" --title TITLE, -t TITLE\n",
271+
" --polite, -p\n"
272+
]
273+
}
274+
],
275+
"source": [
276+
"%%bash\n",
277+
"./greeter.py --help"
278+
]
279+
},
280+
{
281+
"cell_type": "markdown",
282+
"metadata": {},
283+
"source": [
284+
"Before we move into the next section, let's clean up our `if __name__ == \"__main__\":` block by creating a function that keeps the `argparse` magic. We will call that function `process`."
285+
]
286+
},
287+
{
288+
"cell_type": "code",
289+
"execution_count": 10,
290+
"metadata": {},
291+
"outputs": [
292+
{
293+
"name": "stdout",
294+
"output_type": "stream",
295+
"text": [
296+
"Overwriting greeter.py\n"
297+
]
298+
}
299+
],
300+
"source": [
301+
"%%writefile greeter.py\n",
302+
"#!/usr/bin/env python\n",
303+
"from argparse import ArgumentParser\n",
304+
"\n",
305+
"def greet(personal, family, title=\"\", polite=False):\n",
306+
" greeting = \"How do you do, \" if polite else \"Hey, \"\n",
307+
" if title:\n",
308+
" greeting += f\"{title} \"\n",
309+
"\n",
310+
" greeting += f\"{personal} {family}.\"\n",
311+
" return greeting\n",
312+
"\n",
313+
"def process():\n",
314+
" parser = ArgumentParser(description=\"Generate appropriate greetings\")\n",
315+
"\n",
316+
" parser.add_argument('--title', '-t')\n",
317+
" parser.add_argument('--polite', '-p', action=\"store_true\")\n",
318+
" parser.add_argument('personal')\n",
319+
" parser.add_argument('family')\n",
320+
"\n",
321+
" arguments = parser.parse_args()\n",
322+
"\n",
323+
" print(greet(arguments.personal, arguments.family,\n",
324+
" arguments.title, arguments.polite)) \n",
325+
"\n",
326+
"if __name__ == \"__main__\":\n",
327+
" process()"
328+
]
198329
}
199330
],
200331
"metadata": {
@@ -216,7 +347,7 @@
216347
"name": "python",
217348
"nbconvert_exporter": "python",
218349
"pygments_lexer": "ipython3",
219-
"version": "3.8.3-final"
350+
"version": "3.8.5"
220351
},
221352
"widgets": {
222353
"state": {},
@@ -225,4 +356,4 @@
225356
},
226357
"nbformat": 4,
227358
"nbformat_minor": 1
228-
}
359+
}

0 commit comments

Comments
 (0)