2020-05-29 19:26:03 +05:30
|
|
|
[Contents](../Contents.md) \| [Previous (3.4 Modules)](04_Modules.md) \| [Next (3.6 Design Discussion)](06_Design_discussion.md)
|
2020-05-28 10:22:32 -05:00
|
|
|
|
2020-05-25 12:40:11 -05:00
|
|
|
# 3.5 Main Module
|
|
|
|
|
|
|
|
|
|
This section introduces the concept of a main program or main module.
|
|
|
|
|
|
|
|
|
|
### Main Functions
|
|
|
|
|
|
|
|
|
|
In many programming languages, there is a concept of a *main* function or method.
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
// c / c++
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
// java
|
|
|
|
|
class myprog {
|
|
|
|
|
public static void main(String args[]) {
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
This is the first function that executes when an application is launched.
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
### Python Main Module
|
|
|
|
|
|
|
|
|
|
Python has no *main* function or method. Instead, there is a *main*
|
|
|
|
|
module. The *main module* is the source file that runs first.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % python3 prog.py
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
Whatever file you give to the interpreter at startup becomes *main*. It doesn't matter the name.
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
### `__main__` check
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
It is standard practice for modules that run as a main script to use this convention:
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# prog.py
|
|
|
|
|
...
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
# Running as the main program ...
|
|
|
|
|
statements
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-31 12:45:33 -05:00
|
|
|
Statements enclosed inside the `if` statement become the *main* program.
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
### Main programs vs. library imports
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
Any Python file can either run as main or as a library import:
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % python3 prog.py # Running as main
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```python
|
2020-05-28 10:22:32 -05:00
|
|
|
import prog # Running as library import
|
2020-05-25 12:40:11 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In both cases, `__name__` is the name of the module. However, it will only be set to `__main__` if
|
|
|
|
|
running as main.
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
Usually, you don't want statements that are part of the main program
|
|
|
|
|
to execute on a library import. So, it's common to have an `if-`check
|
|
|
|
|
in code that might be used either way.
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
# Does not execute if loaded with import ...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Program Template
|
|
|
|
|
|
|
|
|
|
Here is a common program template for writing a Python program:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# prog.py
|
|
|
|
|
# Import statements (libraries)
|
|
|
|
|
import modules
|
|
|
|
|
|
|
|
|
|
# Functions
|
|
|
|
|
def spam():
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
def blah():
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
# Main function
|
|
|
|
|
def main():
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Command Line Tools
|
|
|
|
|
|
|
|
|
|
Python is often used for command-line tools
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % python3 report.py portfolio.csv prices.csv
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It means that the scripts are executed from the shell /
|
|
|
|
|
terminal. Common use cases are for automation, background tasks, etc.
|
|
|
|
|
|
|
|
|
|
### Command Line Args
|
|
|
|
|
|
|
|
|
|
The command line is a list of text strings.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % python3 report.py portfolio.csv prices.csv
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This list of text strings is found in `sys.argv`.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# In the previous bash command
|
|
|
|
|
sys.argv # ['report.py, 'portfolio.csv', 'prices.csv']
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Here is a simple example of processing the arguments:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
if len(sys.argv) != 3:
|
|
|
|
|
raise SystemExit(f'Usage: {sys.argv[0]} ' 'portfile pricefile')
|
|
|
|
|
portfile = sys.argv[1]
|
|
|
|
|
pricefile = sys.argv[2]
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Standard I/O
|
|
|
|
|
|
|
|
|
|
Standard Input / Output (or stdio) are files that work the same as normal files.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
sys.stdout
|
|
|
|
|
sys.stderr
|
|
|
|
|
sys.stdin
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
By default, print is directed to `sys.stdout`. Input is read from
|
|
|
|
|
`sys.stdin`. Tracebacks and errors are directed to `sys.stderr`.
|
|
|
|
|
|
|
|
|
|
Be aware that *stdio* could be connected to terminals, files, pipes, etc.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % python3 prog.py > results.txt
|
|
|
|
|
# or
|
|
|
|
|
bash % cmd1 | python3 prog.py | cmd2
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Environment Variables
|
|
|
|
|
|
|
|
|
|
Environment variables are set in the shell.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % setenv NAME dave
|
|
|
|
|
bash % setenv RSH ssh
|
|
|
|
|
bash % python3 prog.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`os.environ` is a dictionary that contains these values.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
name = os.environ['NAME'] # 'dave'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Changes are reflected in any subprocesses later launched by the program.
|
|
|
|
|
|
|
|
|
|
### Program Exit
|
|
|
|
|
|
|
|
|
|
Program exit is handled through exceptions.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
raise SystemExit
|
|
|
|
|
raise SystemExit(exitcode)
|
|
|
|
|
raise SystemExit('Informative message')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
An alternative.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import sys
|
|
|
|
|
sys.exit(exitcode)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A non-zero exit code indicates an error.
|
|
|
|
|
|
|
|
|
|
### The `#!` line
|
|
|
|
|
|
|
|
|
|
On Unix, the `#!` line can launch a script as Python.
|
|
|
|
|
Add the following to the first line of your script file.
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# prog.py
|
|
|
|
|
...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It requires the executable permission.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash % chmod +x prog.py
|
|
|
|
|
# Then you can execute
|
|
|
|
|
bash % prog.py
|
|
|
|
|
... output ...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
*Note: The Python Launcher on Windows also looks for the `#!` line to indicate language version.*
|
|
|
|
|
|
|
|
|
|
### Script Template
|
|
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
Finally, here is a common code template for Python programs that run
|
|
|
|
|
as command-line scripts:
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# prog.py
|
|
|
|
|
|
|
|
|
|
# Import statements (libraries)
|
|
|
|
|
import modules
|
|
|
|
|
|
|
|
|
|
# Functions
|
|
|
|
|
def spam():
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
def blah():
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
# Main function
|
|
|
|
|
def main(argv):
|
|
|
|
|
# Parse command line args, environment, etc.
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
import sys
|
|
|
|
|
main(sys.argv)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Exercises
|
|
|
|
|
|
2020-05-26 15:05:19 -05:00
|
|
|
### Exercise 3.15: `main()` functions
|
2020-05-25 12:40:11 -05:00
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
In the file `report.py` add a `main()` function that accepts a list of
|
|
|
|
|
command line options and produces the same output as before. You
|
2022-02-06 19:55:07 -06:00
|
|
|
should be able to run it interactively like this:
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
>>> import report
|
|
|
|
|
>>> report.main(['report.py', 'Data/portfolio.csv', 'Data/prices.csv'])
|
|
|
|
|
Name Shares Price Change
|
|
|
|
|
---------- ---------- ---------- ----------
|
2020-07-04 00:27:04 +02:00
|
|
|
AA 100 9.22 -22.98
|
|
|
|
|
IBM 50 106.28 15.18
|
|
|
|
|
CAT 150 35.46 -47.98
|
|
|
|
|
MSFT 200 20.89 -30.34
|
|
|
|
|
GE 95 13.48 -26.89
|
|
|
|
|
MSFT 50 20.89 -44.21
|
|
|
|
|
IBM 100 106.28 35.84
|
2020-05-25 12:40:11 -05:00
|
|
|
>>>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Modify the `pcost.py` file so that it has a similar `main()` function:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
>>> import pcost
|
|
|
|
|
>>> pcost.main(['pcost.py', 'Data/portfolio.csv'])
|
|
|
|
|
Total cost: 44671.15
|
|
|
|
|
>>>
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-26 15:05:19 -05:00
|
|
|
### Exercise 3.16: Making Scripts
|
2020-05-25 12:40:11 -05:00
|
|
|
|
2020-05-28 10:22:32 -05:00
|
|
|
Modify the `report.py` and `pcost.py` programs so that they can
|
|
|
|
|
execute as a script on the command line:
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bash $ python3 report.py Data/portfolio.csv Data/prices.csv
|
|
|
|
|
Name Shares Price Change
|
|
|
|
|
---------- ---------- ---------- ----------
|
2020-07-04 00:27:04 +02:00
|
|
|
AA 100 9.22 -22.98
|
|
|
|
|
IBM 50 106.28 15.18
|
|
|
|
|
CAT 150 35.46 -47.98
|
|
|
|
|
MSFT 200 20.89 -30.34
|
|
|
|
|
GE 95 13.48 -26.89
|
|
|
|
|
MSFT 50 20.89 -44.21
|
|
|
|
|
IBM 100 106.28 35.84
|
2020-05-25 12:40:11 -05:00
|
|
|
|
|
|
|
|
bash $ python3 pcost.py Data/portfolio.csv
|
|
|
|
|
Total cost: 44671.15
|
|
|
|
|
```
|
2020-05-26 15:05:19 -05:00
|
|
|
|
2020-07-04 00:27:04 +02:00
|
|
|
[Contents](../Contents.md) \| [Previous (3.4 Modules)](04_Modules.md) \| [Next (3.6 Design Discussion)](06_Design_discussion.md)
|