Compiled on: 2024-10-19 — printable version
In order to put everybody on the same page…
… we are going to recall some basic notions and technical aspects related to python and how to handle a python project, namely:
(a.k.a. the shell, the terminal, the console)
The terminal is a text-based interface to the operative system (OS). Each terminal application is executing a shell program
The shell is a program that has a simple job: REPL (Read, Evaluate, Print, Loop)
- it reads a command from the user
- it evaluates the command
- it prints the result
- it loops back to step 1, unless the user explicitly asks to exit
ls
(or dir
on Windows): should list the files in the current directoryecho "Hello World"
: should print “Hello World” on the screenexit
: should close the shell (and the terminal application, if it’s the only shell)The terminal is a powerful tool for software development
It allows developers to interact with the OS in a precise, minimal, and efficient way
Most operations in software development can, and often should, be performed from the terminal
The terminal may accept commands:
- interactively, from the user
- from a script, which is a file containing commands to be executed by a shell
Developers’ mindset:
If a person can do it manually via the shell, then a script can do it automatically
If a script can do it automatically, then that’s the way of doing it
Beware, ‘cause scripts are software too, and they require engineering:
This is not really the case of everyday programming tasks, but let’s keep this in mind
We are going to use the terminal for:
Understaing how to do stuff via the terminal is a reusable skill
We encourage you to read the following lectures from The MIT’s “Missing Semester of Your CS Education”:
bash
, zsh
, fish
, ksh
, csh
, tcsh
, etc.) used by Linux and macOS
cmd
, PowerShell
) which are different from the Unix-like ones, and from each otherWhenever working with the terminal, first thing to do is to understand which shell you are using
- if you’re on Linux, you’re probably using
bash
orzsh
- if you’re on macOS, you’re probably using
zsh
orbash
- if you’re on Windows, you’re probably using
cmd
if you opened the Command Prompt applicationPowerShell
is you opened the PowerShell applicationbash
if are using the Windows Subsystem for Linux (WSL) or Git Bash
Whenver you open a shell, the shell is “in” a directory, which is called the current working directory (CWD)
If one wants to operate on a file in a different directory…
… they have to change the CWD
cd
command (change directory)… without changing the CWD, they have to specify the path to the file
A path is a string that represents the location of a file or a directory in the file system
Beware: path-separator is different among Windows (
\
) and other OS (/
),and we only use
/
in the slides
A relative path is a path that is relative to the CWD
./file.txt
refers to a file named file.txt
in the CWD../file.txt
refers to a file named file.txt
in the parent directory of the CWD./dir/file.txt
refers to a file named file.txt
in a sub-directory of CWD, named dir
An absolute path is a path that starts from the root of the file system
/
/home/giovanni/file.txt
refers to a file named file.txt
in giovanni
’s home directory on Linux/Users/giovanni/file.txt
refers to a file named file.txt
in giovanni
’s home directory on macOSC:
, D:
, etc.)
C:\Users\giovanni\file.txt
refers file file.txt
in giovanni
’s home directory, on drive C:
D:\Data\Photos\profile.jpg
refers file profile.jpg
in the Data\Photos
directory, on drive D:
Operation | *nix | win |
---|---|---|
Print the current directory location | pwd |
echo %cd% |
Remove the file foo (does not work with directories) |
rm foo |
del foo |
Remove directory bar |
rm -r bar |
del bar |
Change disk (e.g., switch to D: ) |
n.a., single root (/ ) |
D: |
Move to the subdirectory baz |
cd baz |
cd baz |
Move to the parent directory | cd .. |
cd.. |
Move (rename) file foo to baz |
mv foo baz |
move foo baz |
Copy file foo to baz |
cp foo baz |
copy foo baz |
Create a directory named bar |
mkdir bar |
md bar |
Most commands have arguments
If you think of commands as functions, then arguments are the parameters of the function
Consider the ls
command
ls
lists the files in the CWD, as an inline listls -l
lists the files in the CWD, as a detailed listls -l /path/to/dir
lists the files in the /path/to/dir
directory, as a detailed listYou should not.
Just try to grasp the basic idea of how shells work
Just memorise that there exist a way to do X
via the shell
X
sYou will eventually memorise the syntax of most frequent commands
For the rest, you can always look up the documentation
Most commands support asking for help when one does not remeber the syntax
COMMAND_NAME --help
or COMMAND_NAME -h
mostly used on Unix-like systemsman COMMAND_NAME
mostly used on Unix-like systems (man is for “manual”)COMMAND_NAME /?
mostly used on WindowsGet-Help COMMAND_NAME
mostly used on WindowsDo not waste your memory, learn how to look up the documentation instead
Some commands are interactive
In this case we say that the command is just starting a process
There is no difference among interactive and non-interactive processes, for the shell
Upon termination, each command returns an exit code (i.e. a non-negative integer)
0
means “everything went fine”When using the shell interactively:
When programming the shell in a script:
$?
if
statement to check the exit code and act accordinglyIn the eyes of the OS, any process can be modelled as follows:
i.e. a black box
stdin
)stdout
)stderr
)more channels may be opened by the process, e.g. when reading / writing files
a stream is an unlimited sequence of bytes (or characters)
Most commonly, for interactive processes, the situation is as follows:
nano
command
nano
is a simple, interactive, text editor for the terminal
Open a shell, and run the following command
nano myfile.txt
This should transform the terminal into a text editor, editing the file myfile.txt
Hello World
Then, press Ctrl+O to save the file
Then, press Ctrl+X to exit the editor
You should be back to the shell, and the file myfile.txt
should have been created
ls
commandPython is a programming language, namely, the reference programming language we use in the course
We will operate Python stuff via the terminal, using the python
command
The command’s behaviour is very different depending on which and how many arguments are passed:
python
with no arguments starts an interactive Python shell
python FILENAME
starts a Python process that executes the Python code in the file FILENAME
python -m MODULE
starts a Python process that executes the module named MODULE
python -c "CODE"
starts a Python process that executes the Python code in the string "CODE"
python -c "print('Hello World')"
Use python --help
to inspect the help of the python
command, and see all the options
When using Python, always remember to check the version of the Python interpreter you are using
python --version
or python -V
Let’s say we are going to build a simple calculator app, in Python
Using Kivy for the GUI, we may easily build the following app:
The source code for such application is available here:
https://github.com/unibo-dtm-se/compact-calculator
ls -la
command)
.python-version
(hidden on Unix) textual declaration of the Python version required by the applicationcalculator.py
: the source code of the applicationrequirements.txt
: a file containing the dependencies of the applicationREADME.md
: a file containing some notes about of the applicationNotice that the calculator.py
file is a Python script that contains only 46 lines of code
Have a look to the source code of the calculator.py
file
Let’s try to run the application
python calculator.py
Traceback (most recent call last):
File "/path/to/your/directory/calculator.py", line 1, in <module>
from kivy.app import App
ModuleNotFoundError: No module named 'kivy'
The issue here is that our application depends on some third-party library, namely Kivy
The solution is pretty simple: let’s install the missing dependency
pip
command, which is the Python package manager
pip install kivy
After the installation, we can try to run the application again
python calculator.py
this time, the application should start, and you should see the calculator Window
play a bit with the application
either in the terminal (ls -la
), or in the GUI, you may notice a new sub-directory named __pycache__
calculator.cpython-3XXX
.pyc
(or similar) in it
the notion of library
the notion of dependency
the notion of runtime
the notion of package manager
the notion of compilation
Basically no programmer ever writes an entire application from scratch
One key principle in SE has always been:
Don’t reinvent the wheel
SE is essentially about how to write good code, which works well, and can be reused in the future
Collections of reusable code are called libraries
All programming languages have a standard library…
math
module in Python, the java.util
package in Java, etc.… plus some mechanism to install and import third-party libraries
pip
command is used to install third-party librariesimport
statement is used to import libraries in the script
The consequences of this “library” idea are manifold
Runtime of a program $\approx$ jargon for “the set of libraries actually available for that program at run-time”
Developers exploit libraries produced by others to avoid wasting time reinventing the wheel
The reasoning is more or less as follows:
A dependency among some software $S$ and some other software $L$
occurs when $S$ requires $L$ to work
Some definitions related to the notion of dependency:
Transitive dependency: if $S$ depends on $L$, and $L$ depends on $M$, then $S$ transitively depends on $M$
Dependency graph of a software $S$: the graph spawned by all the dependencies (direct or transitive) depedencies of $S$
calculator.py
├── Python 3.11.7
└── kivy 2.3.0
├── docutils *
├── kivy-deps-angle >=0.4.0,<0.5.0
├── kivy-deps-glew >=0.3.1,<0.4.0
├── kivy-deps-sdl2 >=0.7.0,<0.8.0
├── kivy-garden >=0.1.4
│ └── requests *
│ ├── certifi >=2017.4.17
│ ├── charset-normalizer >=2,<4
│ ├── idna >=2.5,<4
│ └── urllib3 >=1.21.1,<3
├── pygments *
└── pypiwin32 *
└── pywin32 >=223
To support the extension of runtimes, and therefore the addition of dependencies…
… most programming languages come with 2 related tools:
Packgage $\approx$ a piece of software with a name and a version, and a fixed structure eases installation and reuse
Package managers commonly support specifying from which repository a dependency should be installed
In the Python world:
pip
command is the default package manager, and it is tightly integrated with PyPI
pip install NAME
installs the last version of the package NAME
from PyPIIt is a good practice to document which dependencies a software relies upon
It is even a better practice to automate the installation of dependencies
Other than package managers and repositories, automation requires dependency declaration
In the Python world, there are several conventions for dependency declaration
requirements.txt
file
NAME==VERSION
pip install -r requirements.txt
command installs all depndencis in the file.python-version
file
pyenv
install
command can install corresponding version of PythonThe Python world is not the only one where package managers and package repositories are used
Most programming languages have their own package manager and package repository
In the Linux world, package managers/repositories are used at the OS level too
On MacOS, the Homebrew package manager is widely used (not shipped with the OS)
On Windows, one can use chocolatey or scoop as package managers (not shipped with the OS)
Let’s delve into the actual code of the calculator application
(focus on the comments)
# Import a bunch of stuff from the Kivy library, used below
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
# Matrix of button names and their layout in the GUI
BUTTONS_NAMES = [
['7', '8', '9', '/'],
['4', '5', '6', '*'],
['1', '2', '3', '-'],
['.', '0', '=', '+'],
]
# Calculator *class*: template for all sorts of calculators. this is a particular case of App (i.e. a window, in Kivy)
class CalculatorApp(App):
# Method to build the GUI of the calculator, accordinging to Kivy's conventions
def build(self):
# Definition & initialisation of the "expression" field of the calculator.
# This fields stores a string, representing the expression to be evaluated when "=" is pressed
self.expression = ""
# Let's create a layout, i.e. a virtual container of the visual components the GUI.
# The grid shall dispose components vertically (top to bottom), i.e. it contains *rows* of components
grid = BoxLayout(orientation='vertical')
# Let's create a label, which will serve as the display of the calculator
self._display = Label(text='0', font_size=24, size_hint=(1, 0.75))
# Let's add the label to the grid, as the first row
grid.add_widget(self._display)
# For each *list of* button names in the matrix of button names...
for button_names_row in BUTTONS_NAMES:
# ... let's create another virtual container for a *row* for components
grid_row = BoxLayout()
# ... then, for each button name in the list of button names...
for button_name in button_names_row:
# ... let's create a button, having the button name as text
# (the button is configured to call method on_button_press when pressed)
button = Button(text=button_name, font_size=24, on_press=self.on_button_press)
# ... and let's add the button to the row
grid_row.add_widget(button)
# ... and let's add the row to the grid
grid.add_widget(grid_row)
# Finally, let's return the grid, what will be showed in the window
return grid
# Method to be called when a button is pressed
def on_button_press(self, button):
# If the button is the "=" button
if button.text == '=':
# Try to...
try:
# ... evaluate the expresion *as a Python expression*, convert the result to a string,
# and show that string on the calculator display
self._display.text = str(eval(self.expression))
# If an error occurs in doing the above (e.g. wrong expression)
except SyntaxError:
# ... set the display to "Error"
self._display.text = 'Error'
# Reset the calculator's expression
self.expression = ""
# If the button is any other button
else:
# Append the button's text to the calculator's expression
self.expression += instance.text
# Show the calculator's expression on the display
self._display.text = self.expression
# If the script is executed as a standalone program (i.e. not imported as a module)
if __name__ == '__main__':
# Let's create a new calculator application, and run it
CalculatorApp().run()
calculator.py
, with a single class CalculatorApp
The system is what the user sees (i.e. the view)
The code is not modular: view, controller, and model are mixed together
Requirements may change, e.g.: customers may ask for:
It may be hard to change the model, without breaking the view or the controller
It may be hard to test the model, without testing the view or the controller
The application is, and will always only be, a desktop application