From 7cb63007dd7169d26b608bab0f032c0680f1498b Mon Sep 17 00:00:00 2001 From: Lubomir Dolezal Date: Sun, 7 Jan 2024 12:28:05 +0100 Subject: [PATCH] rework CLI chapter and open a new lesson --- lessons/beginners-en/argparse/index.md | 140 +++++++++++++----- lessons/beginners-en/files/index.md | 24 ++- lessons/beginners-en/json/index.md | 2 +- .../pyladies-en-vienna-2023-autumn/info.yml | 20 +-- 4 files changed, 128 insertions(+), 58 deletions(-) diff --git a/lessons/beginners-en/argparse/index.md b/lessons/beginners-en/argparse/index.md index 5487e53a28..050ad1901b 100644 --- a/lessons/beginners-en/argparse/index.md +++ b/lessons/beginners-en/argparse/index.md @@ -1,49 +1,109 @@ -argparse -======== +Command line interface in Python +======================================= -The `argparse` library is used to create a command line interface - CLI. -Primarily this means program argument processing. It is a part of the standard library, so you do not need to -install anything extra. +In this lesson, we will show you how to create your own command line interface tool `(CLI)` using Python using the `argparse` library. Primarily this means program argument processing. ## Command line interface -... is one of ways how to interact with or control a program (or Python script) as a user - **interface** with it -from inside the computer itself where it is installed (not over the internet). +... is one of ways how to interact with or control a program (not only Python scripts!) as a user - **interface** with it from inside the computer itself where it is installed (not over the internet). That user can be you as a creator of code and someone else you give access to it. +### Motivation + +In the we have shown that sometimes you need to call a Python program +independently with different arguments in order to run computations with different values. + +Letting the tester or user pass these **arguments** from command line (which means not manually editing them inside the Python script) is a great option. The behavior of a program usually is varying - it depends on the instructions -you give to it. +you give to it as **arguments**. + +### Example + +An example of a tool which is usually installed on both Windows and Unix is ``curl``. It allows you to send HTTP requests to the internet and receive responses. -Adding command line arguments to a program enables terminal magic using `find` command like this: +If you want to know what arguments does existing program allow, the argument to use is **help**. This works for most of programs on your computer (if their author created a help page). Also usually there is a `-help` or `--help` command line argument which also shows the expected usage of a tool to any user. + +#### git + +As an example of a tool which works on both Linux and Windows mostly the same way, you can take our beloved [git]({{ lesson_url('git-en/basics') }}). + +Try running this in your terminal: ```console -find . -type f -name "*.txt" -empty -mtime 30 +git --help ``` -Which searches current directory for empty files with .txt suffix and a modification date older than 30 days. -The find command internally has a lot of switches controlling its -behavior while filtering the folder(s) is is searching through -based on user given parameters of the search. +and for any subcommand as well: -### Examples of libraries & argparse +```console +git log --help +``` -Quite many tools for processing arguments from the CLI are also present in the standard Python: -`sys.argv`, `optparse`, `getopt`. -Additionally there is quite commonly used `click` library, which you would need to install via pip -and uses a decorator syntax, that we have not seen yet, so we have decided not to delve into it. +Now that you know that programs usually accept arguments and they are documented in ``help``, you can start using ``git`` in many ways. +For example you can heavily customize output of `git log` in some existing git repository: + +```console +git log --oneline --graph --decorate --cherry-mark --boundary +``` + +Another example of a command line tool with arguments is **find** but does different things on Windows and Linux. This example is valid for Linux: + +```console +find . -type f -name "*.txt" -empty +``` + +Which searches current directory for empty files with .txt suffix. + +### Argparse + +How can we create a CLI in Python? Today, we will show usage of a `argparse` tool. + +It is a part of the standard library, so you do not need to install anything extra. You can find the official documentation on: - [argparse](https://docs.python.org/3/library/argparse.html) -- [sys.argv](https://docs.python.org/3/library/sys.html#sys.argv) -- [optparse](https://docs.python.org/3/library/optparse.html) -- [getopt](https://docs.python.org/3/library/getopt.html) And a quite handy tutorial going through most of functionalities, you could ever encounter: [argparse-tutorial](https://docs.python.org/3/howto/argparse.html) -Here's how to easily create Python command line application with switches: +Additionally there is quite commonly used `click` library, which you would need to install via pip +and uses a decorator syntax, that we have not seen yet, so it can remain as self study. + +#### argparse basic usage + +Usually when we send our Python code to someone else, we do not expect them to read the whole code but reading the help should be enough for them to use it and change parameters. + +For Python CLI tools, you would get the help this way, which you see is the same as for `git`: + +```console +python3 hello.py --help +``` + +Here's how to create Python command line application with switches: + +But first lets start with a function that greets the user for a given number of times and optionally can indent the greeting. + +Lets save the following code into `hello.py` file. + +```python +def hello(count, name, indent=False): + """Simple program that greets 'name' for a total of 'count' times and optionally indents.""" + for _ in range(count): + if indent: + print(" ", end="") + print(f"Hello {name}!") + +count = 5 +name = "Tyna" +indent = True +hello(count, name, indent) +``` + +And run it as usual as `python hello.py`. + +Now we want the arguments `count`, `name` and `indent` to be enabled as CLI options and come from command line arguments when you start the script. ```python import argparse @@ -53,8 +113,6 @@ parser.add_argument('-n', '--name', help='a name to repeat', required=True) parser.add_argument('-c', '--count', help='how many times', required=True, type=int) parser.add_argument("--indent", action="store_true", help=("name will be indented by 4 spaces")) -args = parser.parse_args() - def hello(count, name, indent=False): """Simple program that greets 'name' for a total of 'count' times and optionally indents.""" for _ in range(count): @@ -62,11 +120,12 @@ def hello(count, name, indent=False): print(" ", end="") print(f"Hello {name}!") +args = parser.parse_args() hello(args.count, args.name, args.indent) ``` The first step in using the argparse is creating an `ArgumentParser` object with some description. -Then you fill an ArgumentParser with information about program +Then you fill an `ArgumentParser` with information about program arguments, which is done by making calls to the `add_argument()` method. This information is stored and used when `parse_args()` is called. @@ -74,8 +133,6 @@ You can set parameters as required by adding `required=True` option. It is also possible to their `type`, which will try to convert the variable to the data type announced. In order to allow simple storing of boolean flags `True/False`, you can use the `action="store_true"` parameter. -Try it! If you have it saved as `hello.py`, try: - ```console python3 hello.py python3 hello.py --help @@ -89,16 +146,31 @@ That is already a very solid first program is it not? ## Positional arguments -You can of course define arguments, which are `positional` in the same way as when you are defining and calling -a function. The parsing will expect all arguments to be in the order, you defined them. +You can of course define arguments, which are `positional` in the same way as when you are defining and using function arguments. The parsing will expect all arguments to be in the order, in which you defined them. + +To try it, replace the first two lines with `name` and `count` arguments with following lines: ```python -parser.add_argument("input_file", default=None, help=("Input file to read")) +parser.add_argument("name", help='a name to repeat') +parser.add_argument("count", help='how many times', type=int) +``` + +From now on, the order in which you provide `name` and `count` arguments will be important. The named arguments can still be provided before or after the positional arguments. + +```console +python3 hello.py PyLady 5 --indent ``` +An example of a wrong call would be: + ```console -python3 hello.py input.txt --count 5 --name PyLady +python3 hello.py 5 PyLady +``` +Which should run into following error: + +``` +hello.py: error: argument count: invalid int value: 'PyLady' ``` ## Other options @@ -107,6 +179,8 @@ Switch names begin, according to Unix convention, with hyphens: one hyphen `-` for one-letter abbreviations, two hyphens `--` for multi-letter names. One switch can have more than one name - short option and long option. +This example shows how it is usually done for example of `logging` setup - although it does not apply for our simple example. + ```python parser.add_argument( "-v", "--verbosity", type=int, default=3, choices=[0, 1, 2, 3, 4], @@ -118,7 +192,7 @@ parser.add_argument( ``` Parameter names with `hyphens` inside them will automatically turn them into variable names -with `underscores`, as it is not possible to have a `hyphen` in variable name in Python. +with `underscores`, as it is not possible to have a `hyphen -` in variable name in Python. ```python parser.add_argument( diff --git a/lessons/beginners-en/files/index.md b/lessons/beginners-en/files/index.md index 46d87306fe..150ed48624 100644 --- a/lessons/beginners-en/files/index.md +++ b/lessons/beginners-en/files/index.md @@ -12,19 +12,11 @@ Create a `poem.txt` file in the editor and write any poem into it. Save the file. > [note] -> I recommend to use the same editor that you use for your -> Python program to edit the file with the poem. -> -> If you use a different editor than Atom, be sure to keep in mind when coding: -> * If the editor offers you a choice of encoding, choose `UTF-8`. -> * If `UTF-8 without BOM` is available, use it. -> * If you have to use Notepad then use the non-standard `utf-8-sig` instead. -> -> [`utf-8`] is the name of the standard encoding. -> You can store any emoji or accented characters to files with this encoding. -> 🎉 - -[`utf-8`]: https://en.wikipedia.org/wiki/UTF-8 +> In the following example, we are using encoding `utf-8`, which +> covers majority of european characters. +> [`utf-8`]: https://en.wikipedia.org/wiki/UTF-8 +> There are other encodings covering different character sets. +> If you want to learn more about encodings, you can read this nicely readable [Encoding article](https://tonsky.me/blog/unicode) Let's create a following code: @@ -235,7 +227,11 @@ with open('json_data.txt') as json_file: print(data) ``` -In order to read or write `csv` you can use the inbuilt `csv` library. +In previous lesson [json]({{ lesson_url('beginners-en/json') }}) we have shown also loading JSON from a string and writing to a string, which used methods `loads` and `dumps`. The ending `s` represents `string`. You can remember the difference between `load` and `loads` like that. + +## Other common data formats + +In order to read or write `csv` you can use the built-in `csv` library. For other data formats like `Excel` files, you need to install external libraries. diff --git a/lessons/beginners-en/json/index.md b/lessons/beginners-en/json/index.md index e56ab899aa..4af5a1e864 100644 --- a/lessons/beginners-en/json/index.md +++ b/lessons/beginners-en/json/index.md @@ -81,7 +81,7 @@ which, for its simplicity, has expanded the most: ## JSON in Python Object encoding in JSON is simple: there is a `json` module, -whose `load` method retrieves data from the string: +whose `loads` method retrieves data from the string: ```python import json diff --git a/runs/2023/pyladies-en-vienna-2023-autumn/info.yml b/runs/2023/pyladies-en-vienna-2023-autumn/info.yml index 045ae9edc3..0760bdb20b 100644 --- a/runs/2023/pyladies-en-vienna-2023-autumn/info.yml +++ b/runs/2023/pyladies-en-vienna-2023-autumn/info.yml @@ -220,16 +220,16 @@ plan: - title: Working with Files and CLI slug: files_cli date: 2024-01-09 - # materials: - # - lesson: beginners-en/json - # - lesson: beginners-en/files - # - lesson: beginners-en/argparse - # - title: Files/Exceptions cheatsheet - # url: https://github.com/ehmatthes/pcc/releases/download/v1.0.0/beginners_python_cheat_sheet_pcc_files_exceptions.pdf - # type: cheatsheet - # - title: Homework (PDF) - # url: https://drive.google.com/file/d/1y04_M3994x9GaS19IyYyqIAe0N4YdTI1/view - # type: homework + materials: + - lesson: beginners-en/json + - lesson: beginners-en/files + - lesson: beginners-en/argparse + - title: Files/Exceptions cheatsheet + url: https://github.com/ehmatthes/pcc/releases/download/v1.0.0/beginners_python_cheat_sheet_pcc_files_exceptions.pdf + type: cheatsheet + - title: Homework + url: https://docs.google.com/document/d/1Tjy0uSqdVMmQI2tbHWBmDtrnwMl-Px3J7-N6_xRh12o + type: homework - title: Final Project slug: final_project