|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "# Scripting & the shell\n", |
| 8 | + "Throughout ESPIn, we've been using Jupyter notebooks to organize our code. Notebooks are great tools for exploring new ideas, prototyping models, and taking a look at model output. But, as you might have noticed by now, notebooks are pretty slow, especially when we start to run programs with hundreds or thousands of lines of code. \n", |
| 9 | + "\n", |
| 10 | + "This lesson has two parts: first we'll talk about the *shell*, which provides a text-based interface for interacting with our machine. Then, we'll cover how to run a simple diffusion model in a *script*, as opposed to a notebook.\n", |
| 11 | + "\n", |
| 12 | + "#### Objectives\n", |
| 13 | + "- Learn how to navigate in the shell and perform simple commands\n", |
| 14 | + "- Create a Python script to run a diffusion model\n", |
| 15 | + "- Understand how to use scripting in your own workflow" |
| 16 | + ] |
| 17 | + }, |
| 18 | + { |
| 19 | + "cell_type": "markdown", |
| 20 | + "metadata": {}, |
| 21 | + "source": [ |
| 22 | + "# Shell commands" |
| 23 | + ] |
| 24 | + }, |
| 25 | + { |
| 26 | + "cell_type": "markdown", |
| 27 | + "metadata": {}, |
| 28 | + "source": [ |
| 29 | + "### Exercise 1) Open a terminal.\n", |
| 30 | + "A *terminal* is a text interface that allows a user to communicate with the operating system. We have a few different examples of terminals:\n", |
| 31 | + "- JupyterLab's Terminal app\n", |
| 32 | + "- Linux terminal or xterm\n", |
| 33 | + "- macOS Terminal or iTerm\n", |
| 34 | + "- Windows Git Bash\n", |
| 35 | + "- IDE's like VS Code, Spyder, or Cursor often have built-in terminals\n", |
| 36 | + "\n", |
| 37 | + "A *shell* is the software responsible for interpreting your instructions. The most common shell is Bash (Bourne Again SHell). If you're on a Unix-based OS (Mac or Linux), you'll have Bash installed natively. If you're on Windows, you can get it through Git for Windows, which includes Git Bash.\n", |
| 38 | + "\n", |
| 39 | + "The fundamental action in a terminal is a REPL: Read, Evaluate, Print Loop. First, we have to type something. Try typing <tt>pwd</tt>, to print the current working directory. When you hit enter, the shell:\n", |
| 40 | + "1. Reads the command.\n", |
| 41 | + "2. Evaluates the command. \n", |
| 42 | + "3. Prints output to the terminal.\n", |
| 43 | + "\n", |
| 44 | + "Then, we repeat the process. You don't need to learn everything about the shell all at once, since you'll pick things up as you need them. We'll start with the basics here, and I'll include a few additional tips and tricks below. \n", |
| 45 | + "\n", |
| 46 | + "### Exercise 2) Bash basics.\n", |
| 47 | + "For now, let's go through the [brief shell lesson](https://github.com/csdms/ivy/blob/main/lessons/shell/short-shell.md)." |
| 48 | + ] |
| 49 | + }, |
| 50 | + { |
| 51 | + "cell_type": "markdown", |
| 52 | + "metadata": {}, |
| 53 | + "source": [ |
| 54 | + "### Random tips and tricks" |
| 55 | + ] |
| 56 | + }, |
| 57 | + { |
| 58 | + "cell_type": "markdown", |
| 59 | + "metadata": {}, |
| 60 | + "source": [ |
| 61 | + "- the up-arrow or !! repeats the previous command\n", |
| 62 | + " - sudo !! is very useful\n", |
| 63 | + "- Ctrl+R searches your command history\n", |
| 64 | + "- Ctrl+C interrupts the current command\n", |
| 65 | + "- Ctrl+A goes to the start of a line; Ctrl+E goes to the end\n", |
| 66 | + "- Ctrl+L clears the terminal window\n", |
| 67 | + "- grep searches files and directories\n", |
| 68 | + "- prepend time to a command to see how long it takes\n", |
| 69 | + "- use brace expansion to quickly perform multiple operations at once\n", |
| 70 | + " - for example, mkdir {foo,bar,baz} makes 3 directories named foo, bar, and baz" |
| 71 | + ] |
| 72 | + }, |
| 73 | + { |
| 74 | + "cell_type": "markdown", |
| 75 | + "metadata": {}, |
| 76 | + "source": [ |
| 77 | + "# Text editors and IDE's" |
| 78 | + ] |
| 79 | + }, |
| 80 | + { |
| 81 | + "cell_type": "markdown", |
| 82 | + "metadata": {}, |
| 83 | + "source": [ |
| 84 | + "Let's talk about how to write code outside of a Jupyter notebook. Here's a [brief lesson](https://github.com/csdms/ivy/blob/main/lessons/editors/index.md).\n", |
| 85 | + "\n", |
| 86 | + "The TL;DR is:\n", |
| 87 | + "\n", |
| 88 | + "Code is text. \n", |
| 89 | + "\n", |
| 90 | + "Write code with a text editor. \n", |
| 91 | + "\n", |
| 92 | + "Find one you like and learn to use it well. \n", |
| 93 | + "\n", |
| 94 | + "Don't write code with a word processor. \n", |
| 95 | + "\n", |
| 96 | + "Plain text is future-proof. " |
| 97 | + ] |
| 98 | + }, |
| 99 | + { |
| 100 | + "cell_type": "markdown", |
| 101 | + "metadata": {}, |
| 102 | + "source": [ |
| 103 | + "# Python scripts" |
| 104 | + ] |
| 105 | + }, |
| 106 | + { |
| 107 | + "cell_type": "markdown", |
| 108 | + "metadata": {}, |
| 109 | + "source": [ |
| 110 | + "### Exercise 3) Write a Python script.\n", |
| 111 | + "Now for the fun part! Let's take our finalized diffusion code from the functions lesson and turn it into a script. \n", |
| 112 | + "\n", |
| 113 | + "First, make a new plain text file. Name it something descriptive and include \".py\" as the file type. This tells the system to treat our plain text file as a Python script - as we'll see in a moment.\n", |
| 114 | + "\n", |
| 115 | + "Then, take your finalized code and copy it into the script. Make sure to include all of the import statements that you'll need!\n", |
| 116 | + "\n", |
| 117 | + "Finally, let's run our script! Open a terminal in JupyterHub, and then:\n", |
| 118 | + "1. Figure out which directory you are in.\n", |
| 119 | + "2. Run <tt>\"python3 SCRIPT.py\"</tt> where SCRIPT is the name of your script.\n", |
| 120 | + "\n", |
| 121 | + "*Note: it's possible that this could run without throwing an error, but still not display any plots. If that happens, you'll need to add a line that saves the matplotlib figures. The syntax for this is:*\n", |
| 122 | + "\n", |
| 123 | + "plt.savefig('path-to-file') \n", |
| 124 | + "\n", |
| 125 | + "*where path-to-file is wherever you would like to save the image. Make sure to include that line before any plt.show() commands, so that you save the current figure before clearing it.*\n" |
| 126 | + ] |
| 127 | + }, |
| 128 | + { |
| 129 | + "cell_type": "markdown", |
| 130 | + "metadata": {}, |
| 131 | + "source": [ |
| 132 | + "\n", |
| 133 | + "### Exercise 4) Include a main() function.\n", |
| 134 | + "Now, what you've just done works perfectly well for one-off scripts. But, when we talk later about modules and packages, you'll see some cases where it might be advantageous to separate out the *definitions* from the *actions* in a script. For example, you might want to write some new code that pulls in the functions you defined in this script, but doesn't go ahead and make all of the plots or actually run the model. This is such a common use case that Python has a special syntax for handling it.\n", |
| 135 | + "\n", |
| 136 | + "At the bottom of your script, write the following code:\n", |
| 137 | + "\n", |
| 138 | + "<tt> \n", |
| 139 | + "if __name__ == '__main__':\n", |
| 140 | + "</tt>\n", |
| 141 | + "\n", |
| 142 | + "This chunk of code checks for two reserved keywords in Python: *\\_\\_name\\_\\_* and *\\_\\_main\\_\\_*. Basically, \\_\\_name\\_\\_ is the name of the current module, and \\_\\_main\\_\\_ is the name of the current program being run. When you run a script using <tt>python3 foo.py</tt>, then *foo* is both \\_\\_name\\_\\_ and \\_\\_main\\_\\_. So, it will run everything you have defined in that *if* block. But, if you instead import something from <tt>foo.py</tt> into <tt>bar.py</tt> and run <tt>bar.py</tt>, then \\_\\_name\\_\\_ is *foo* but \\_\\_main\\_\\_ is *bar*, so the code inside the *if* block won't run.\n", |
| 143 | + "\n", |
| 144 | + "For this exercise, separate out the *definitions* in your script from the *actions*. Leave the definitions above, but move the actions (running the model and plotting figures) into a protected *if* block, using the syntax from above.\n", |
| 145 | + "\n", |
| 146 | + "### Exercise 5) Import a function into a new script.\n", |
| 147 | + "Now we have a great setup for reusing our code! Make a new script in the same directory as your old one. At the top of the script include an import statement that looks like:\n", |
| 148 | + "\n", |
| 149 | + "<tt>\n", |
| 150 | + "from my_script import my_function\n", |
| 151 | + "</tt>\n", |
| 152 | + "\n", |
| 153 | + "Note that you shouldn't include any of the file extensions here. For the rest of this exercise, build out the second script to use the functions you defined previously. Try a few new ideas (check out the additional exercises for inspiration), so you can get used to modifying your scripts, running them, and checking the output." |
| 154 | + ] |
| 155 | + }, |
| 156 | + { |
| 157 | + "cell_type": "markdown", |
| 158 | + "metadata": {}, |
| 159 | + "source": [ |
| 160 | + "# Discussion\n", |
| 161 | + "What are the advantages and disadvantages of notebooks and scripts? \n", |
| 162 | + "\n", |
| 163 | + "What type of workflow makes the most sense for you? \n", |
| 164 | + "\n", |
| 165 | + "Does that change at all throughout the lifetime of a project?" |
| 166 | + ] |
| 167 | + } |
| 168 | + ], |
| 169 | + "metadata": { |
| 170 | + "kernelspec": { |
| 171 | + "display_name": "espin-2025-i_K4nWSq-py3.13", |
| 172 | + "language": "python", |
| 173 | + "name": "python3" |
| 174 | + }, |
| 175 | + "language_info": { |
| 176 | + "name": "python", |
| 177 | + "version": "3.13.2" |
| 178 | + } |
| 179 | + }, |
| 180 | + "nbformat": 4, |
| 181 | + "nbformat_minor": 2 |
| 182 | +} |
0 commit comments