Chapter 15 Quiz: Building Command-Line Tools and Scripts
Test your understanding of CLI development concepts. Each question has one best answer unless stated otherwise.
Question 1
What is the primary benefit of separating business logic from the CLI layer in a command-line tool?
A) It makes the code run faster B) It reduces the number of files in the project C) It allows the logic to be tested independently and reused in other contexts D) It eliminates the need for argument parsing
Answer
**C) It allows the logic to be tested independently and reused in other contexts** When business logic is decoupled from the CLI framework, you can write unit tests that call the logic functions directly without simulating command-line invocations. The same logic can also be reused in a web API, a GUI, or a library without modification.Question 2
Which of the following is a correct way to create a boolean flag with argparse?
A) parser.add_argument("--verbose", type=bool)
B) parser.add_argument("--verbose", action="store_true")
C) parser.add_argument("--verbose", flag=True)
D) parser.add_argument("--verbose", boolean=True)
Answer
**B) `parser.add_argument("--verbose", action="store_true")`** `action="store_true"` sets the value to `True` when the flag is present and defaults to `False`. Using `type=bool` is a common mistake -- it would try to call `bool()` on the string argument, and `bool("False")` evaluates to `True`.Question 3
In click, what is the purpose of @click.pass_context?
A) It passes the current working directory to the function B) It passes the click Context object, which can store shared state between commands C) It passes all command-line arguments as a dictionary D) It enables error handling for the decorated function
Answer
**B) It passes the click Context object, which can store shared state between commands** The Context object in click (`ctx`) acts as a container for shared state. A parent group command can store values (like `verbose=True`) in `ctx.obj`, and child commands can access them via `@click.pass_context`.Question 4
In a configuration hierarchy, which source should have the HIGHEST priority?
A) Built-in defaults B) Configuration file C) Environment variables D) Command-line flags
Answer
**D) Command-line flags** The standard precedence is: defaults < config file < environment variables < CLI flags. Command-line flags have the highest priority because they represent the user's explicit intent for a specific invocation.Question 5
Why should logging messages be sent to stderr rather than stdout?
A) stderr is faster than stdout B) It prevents logging from contaminating the tool's output when piped to other commands C) stderr supports color output but stdout does not D) Python's logging module only supports stderr
Answer
**B) It prevents logging from contaminating the tool's output when piped to other commands** When a user pipes output (e.g., `mytool list | grep pattern`), only stdout is piped. If logging goes to stderr, diagnostic messages like "Processing 42 files..." appear on the terminal but do not interfere with the piped data.Question 6
What does the action="count" parameter do in argparse?
A) Counts the number of positional arguments
B) Counts how many times a flag is used (e.g., -v, -vv, -vvv)
C) Validates that the argument is a number
D) Counts the number of subcommands
Answer
**B) Counts how many times a flag is used (e.g., `-v`, `-vv`, `-vvv`)** `action="count"` increments a counter each time the flag appears. This is commonly used for verbosity levels where `-v` means verbose, `-vv` means very verbose, and `-vvv` means debug-level output.Question 7
What is the conventional exit code for a program terminated by Ctrl+C (SIGINT)?
A) 0 B) 1 C) 2 D) 130
Answer
**D) 130** By Unix convention, when a process is killed by signal N, the exit code is 128 + N. SIGINT is signal 2, so the exit code is 128 + 2 = 130.Question 8
What is the purpose of pip install -e . (editable mode)?
A) It installs the package in a way that requires elevated permissions B) It installs the package so that source code changes are reflected immediately without reinstalling C) It installs only the executable entry point, not the package D) It installs the package in encrypted mode for security
Answer
**B) It installs the package so that source code changes are reflected immediately without reinstalling** Editable mode creates a link to your source directory instead of copying files. This means changes you make to the code take effect immediately, which is essential during development.Question 9
Which Python standard library module was added in Python 3.11 for reading TOML files?
A) toml
B) tomllib
C) configparser
D) tomlreader
Answer
**B) `tomllib`** Python 3.11 added `tomllib` to the standard library for reading TOML files. For Python 3.10 and earlier, the third-party `tomli` package provides the same API. Note that `tomllib` is read-only -- it cannot write TOML files.Question 10
What is the purpose of click.Path(exists=True) as a type parameter?
A) It converts the argument to a Path object B) It validates that the provided path exists on the filesystem before the command runs C) It creates the directory if it does not exist D) It resolves the path to an absolute path
Answer
**B) It validates that the provided path exists on the filesystem before the command runs** `click.Path(exists=True)` tells click to verify the path exists before calling the command function. If the path does not exist, click automatically shows an error message and exits, eliminating the need for manual validation code.Question 11
When processing a very large file (several gigabytes), which I/O pattern should you use?
A) Read the entire file into memory with .read()
B) Use streaming (line-by-line) processing
C) Copy the file to a smaller temporary file first
D) Use json.load() to parse it
Answer
**B) Use streaming (line-by-line) processing** Streaming processes the file one line (or chunk) at a time, keeping memory usage constant regardless of file size. Reading a multi-gigabyte file entirely into memory would likely exhaust available RAM and crash the program.Question 12
What does this pyproject.toml entry do?
[project.scripts]
fileutil = "fileutil.cli:main"
A) It defines a Python import alias
B) It creates an executable command that calls the main function from fileutil.cli
C) It runs fileutil.cli:main during package installation
D) It adds fileutil as a dependency
Answer
**B) It creates an executable command that calls the `main` function from `fileutil.cli`** The `[project.scripts]` section defines console script entry points. When the package is installed via pip, a wrapper executable named `fileutil` is created that calls the `main()` function from the `fileutil.cli` module.Question 13
What is the purpose of the __main__.py file in a Python package?
A) It marks the directory as a Python package
B) It allows the package to be run with python -m packagename
C) It is automatically executed when the package is imported
D) It defines the package's main class
Answer
**B) It allows the package to be run with `python -m packagename`** When you run `python -m mypackage`, Python looks for `__main__.py` inside the package and executes it. This provides an alternative way to run the tool without installing it as a console script.Question 14
Which of the following is the safest way to load a YAML configuration file?
A) yaml.load(file)
B) yaml.load(file, Loader=yaml.FullLoader)
C) yaml.safe_load(file)
D) yaml.unsafe_load(file)
Answer
**C) `yaml.safe_load(file)`** `yaml.safe_load()` only loads basic Python types (strings, numbers, lists, dicts) and prevents execution of arbitrary Python code. `yaml.load()` without a safe Loader can be exploited to execute malicious code embedded in a YAML file.Question 15
How does the rich library handle output when stdout is redirected to a file?
A) It crashes with an error B) It automatically disables formatting and colors, producing plain text C) It produces ANSI escape codes in the file D) It refuses to output anything
Answer
**B) It automatically disables formatting and colors, producing plain text** Rich's `Console` class detects whether stdout is a terminal (TTY). When output is redirected to a file or pipe, it automatically strips colors and formatting, producing clean plain text. This can be overridden with `force_terminal=True` if needed.Question 16
What is the correct order for error handling in a CLI main() function?
A) Catch Exception first, then CLIError, then KeyboardInterrupt
B) Catch KeyboardInterrupt first, then CLIError, then Exception
C) Catch CLIError first, then KeyboardInterrupt, then Exception
D) Order does not matter for exception handling
Answer
**C) Catch `CLIError` first, then `KeyboardInterrupt`, then `Exception`** Python matches except clauses in order. Since `CLIError` is the most specific application error, it should be caught first to provide targeted error messages. `KeyboardInterrupt` should be caught before the generic `Exception` because it inherits from `BaseException`, not `Exception`, though explicitly catching it ensures clean Ctrl+C behavior. The generic `Exception` clause acts as a safety net for unexpected errors.Question 17
What does the nargs="+" parameter mean in argparse?
A) The argument is optional B) The argument accepts one or more values C) The argument accepts zero or more values D) The argument's value must be a positive number
Answer
**B) The argument accepts one or more values** `nargs="+"` means the argument must receive at least one value but can receive multiple. The values are collected into a list. Use `nargs="*"` for zero or more values.Question 18
Why should you provide non-interactive alternatives for every interactive prompt in a CLI tool?
A) Interactive prompts are slower B) Interactive prompts cannot display colors C) Tools with mandatory interactive prompts cannot be used in scripts, cron jobs, or CI pipelines D) Interactive prompts are deprecated in Python
Answer
**C) Tools with mandatory interactive prompts cannot be used in scripts, cron jobs, or CI pipelines** Automated environments have no human to answer prompts. A tool that blocks waiting for input will hang indefinitely in a CI pipeline or cron job. Flags like `--yes`, `--force`, or `--non-interactive` allow the tool to work in both interactive and automated contexts.Question 19
What is an "atomic write" and why is it important for CLI tools?
A) A write operation that uses the smallest possible buffer B) A write that completes entirely or not at all, preventing data corruption from interruptions C) A write that uses atomic-level encryption D) A write that is performed in a single CPU instruction
Answer
**B) A write that completes entirely or not at all, preventing data corruption from interruptions** An atomic write is implemented by writing to a temporary file first and then renaming it to the target path. Since file rename is an atomic operation on most filesystems, if the process crashes during writing, the original file remains intact. The temporary file either gets renamed (success) or is left as an orphan that can be cleaned up.Question 20
Which of the following is the BEST error message for a CLI tool?
A) Error: EACCES
B) Error: Something went wrong.
C) PermissionError: [Errno 13] Permission denied: '/etc/config.toml'
D) Error: Cannot read '/etc/config.toml': permission denied. Try running with sudo or check file permissions with 'ls -la /etc/config.toml'.
Answer
**D) `Error: Cannot read '/etc/config.toml': permission denied. Try running with sudo or check file permissions with 'ls -la /etc/config.toml'.`** The best error messages follow the formula: what went wrong + why + what to do about it. Option D tells the user the specific file that failed, the reason (permission denied), and provides actionable steps to resolve the issue.Question 21
What is the purpose of RotatingFileHandler in the logging module?
A) It rotates the log entries in a circular buffer B) It automatically archives log files when they reach a size limit and creates new ones C) It rotates the log level between DEBUG, INFO, and WARNING D) It logs to multiple files simultaneously
Answer
**B) It automatically archives log files when they reach a size limit and creates new ones** `RotatingFileHandler` monitors the log file size. When it exceeds `maxBytes`, the current file is renamed (e.g., `app.log.1`), and a new `app.log` is created. The `backupCount` parameter controls how many old files to keep.Question 22
In click, what is the difference between @click.argument and @click.option?
A) Arguments are named with --, options are positional
B) Arguments are positional and required by default; options are named with -- and optional by default
C) There is no difference; they are aliases
D) Arguments accept multiple values; options accept only one
Answer
**B) Arguments are positional and required by default; options are named with `--` and optional by default** In click, `@click.argument("filename")` creates a positional parameter (e.g., `mytool file.txt`), while `@click.option("--output")` creates a named parameter (e.g., `mytool --output file.txt`). Arguments are required by default; options are optional by default but can be made required.Question 23
What does sys.stdout.isatty() check?
A) Whether the program is running as root B) Whether stdout is connected to a terminal (as opposed to a pipe or file) C) Whether the terminal supports Unicode D) Whether the terminal supports color output
Answer
**B) Whether stdout is connected to a terminal (as opposed to a pipe or file)** `isatty()` returns `True` when stdout is a terminal and `False` when it is redirected to a file or pipe. This is commonly used to decide whether to display rich formatting (terminal) or plain text (pipe/file).Question 24
When building a CLI tool incrementally with AI assistance, what is the recommended first step?
A) Implement all business logic B) Set up the packaging with pyproject.toml C) Define the CLI interface skeleton with commands, arguments, and help text D) Add comprehensive error handling
Answer
**C) Define the CLI interface skeleton with commands, arguments, and help text** Starting with the interface (commands, arguments, options, and help text) follows the specification-driven approach. It establishes what the tool does from the user's perspective before implementing how it does it. This skeleton can be tested and iterated on quickly, and each command can be implemented independently.Question 25
What is the XDG Base Directory specification?
A) A Python packaging standard for executable scripts B) A standard for where applications should store configuration, data, and cache files on Unix systems C) A specification for XML-based configuration files D) A standard for directory permissions on Linux