About this articleThis article was created using an automated generation workflow powered by generative AI. We reviewed Python's official argparse and boolean specifications, and reproduced argument conversion pitfalls using only the standard library.
Verification Status: 🧪 Logic verified via local Python execution. Not yet verified for Windows, macOS, or production job deployment.
Why does Python's –dry-run False evaluate to True? Safely fixing boolean specifications in argparse
Even though you intended to provide a command-line tool option meaning "dry-run without making changes" like--dry-runeven when typing--dry-run Falseit ends up asTrueThe cause is thatargparseintype=boolis not a feature that converts the string "False" into a boolean false value.
This misunderstanding is not merely an issue of display. If you use the same syntax for critical flags—such as whether to allow overwriting files, sending notifications, or executing deletions—there is a risk that the operator's intended state and the program's actual state will be reversed. This article reproduces the phenomenon using short code that does not modify any files or external services, and shows how to redesign arguments to avoid ambiguity.
- Target environment and verification items
- First, reproduce the incorrect specification
- Why False evaluates to true
- Correct design pattern 1: Checking only for the presence of a flag
- Correct design pattern 2: Providing both positive and negative forms
- Visualizing what is treated as a boolean
- is a name expressing the intent to "disable confirmation mode." What the diagram illustrates is not which is technically superior, but rather the difference in design that preserves the user's input intent.
- Troubleshooting failures: Check three places
- How to revise operational manuals and monitoring logs
- Official documentation and verification scope
Target environment and verification items
| Item | Current condition |
|---|---|
| Utilized components | Python 3 standard libraryargparse |
| Comparison method | type=bool、store_true、BooleanOptionalAction |
| Version note | BooleanOptionalActionis Python 3.9 or later |
| safe range | Python strings and screen output only. No file creation, deletion, or network communication. |
| Expected result | Falsefrom the charactersTruecan explain why is created |
In operational scripts, it is common to switch between a dry run that only displays the list of target files and a live run that actually applies changes. What makes a failure here problematic is when you intend to specify dry-run mode, but it gets accidentally disabled. The code in this article addresses that switchingsimply prints the valueso running any case will not touch actual files.
First, reproduce the incorrect specification
The following code is an example specifyingtype=bool.parse_args()Since argument arrays can be passed directly to , you can safely compare the same conditions without repeatedly modifying the terminal command. The leading empty array represents the case where no options are specified at all.
import argparse
parser = argparse.ArgumentParser(prog="sample")
parser.add_argument("--dry-run", type=bool, default=False)
for supplied in (
[],
["--dry-run", "False"],
["--dry-run", "0"],
["--dry-run", ""],
):
result = parser.parse_args(supplied)
print(repr(supplied), "=>", result.dry_run)
When executed, the empty array evaluates toFalseand the string"False"also becomes"0".TrueSurprisingly, only empty strings like["--dry-run", ""]evaluate toFalse. In other words, rearranging words that humans consider to represent "false" will not fix the issue. The lowercasefalseornoeven if replaced with , it evaluates to true as long as the string is not empty.
If the user intended to--dry-run Falsedisable the test run, the test run is actually still enabled. On the other hand,--delete Falseor--allow-write Falseif dangerous operations like these are authorized using the same method, an unintended authorization might occur. Both are design-related flaws.
Why False evaluates to true
Python's built-inbool()does not interpret the semantic meaning of strings in English. It only evaluates whether the string is empty.bool("")is false, butbool("False")、bool("0")、bool("no")are both true. If it contains even a single character, it is generally evaluated as a non-empty string and thus true.
argparse'stype=is a mechanism that passes the input string to the specified conversion function. Therefore,type=booldoes not readFalseas a pre-registered value, but ratherbool("False")results in the same outcome as calling. This is not a bug inargparsebut rather the result of combiningbool()'s specification with the argument conversion mechanism.
Understanding this principle explains why--dry-run 0fails to fix it. The numerical value0tobool(0)if passed to, it evaluates to false, but the command-line"0"is first passed as a string. Here, the types differ. Even with visually similar values, failing to check the type received by the program leads to incorrect conclusions.
Correct design pattern 1: Checking only for the presence of a flag
For example, for a--verboseto output verbose logs, usingaction="store_true"is concise. It treats the presence of the option as true and its absence as false. There is no need to append a value.--verbose FalseEnteringFalsewill be rejected as an unexpected argument, thereby avoiding ambiguous interpretation.
However, you also need to communicate to users how to disable the flag. If the default is originally false, omitting the option effectively disables it. In systems where the default is true, relying solely on the presence or absence of a flag makes it difficult to express the requirement to explicitly set it to false.
The key point is thatstore_trueis not always the safest approach. The goal is to determine what options allow and prohibit, and then design clear user operations.
Correct design pattern 2: Providing both positive and negative forms
In Python 3.9 and later, you can useargparse.BooleanOptionalAction. This allows you to define two forms:--dry-runand--no-dry-run. There is no need to read and convert string"False"values; the option names themselves clearly convey the intent.
Next is an example where confirmation mode is enabled by default unless specified otherwise. This action only displays output on the screen and does not include actual write operations.
import argparse
parser = argparse.ArgumentParser(prog="sample")
parser.add_argument(
"--dry-run",
action=argparse.BooleanOptionalAction,
default=True,
help="変更しない確認モード。既定は有効",
)
for supplied in ([], ["--dry-run"], ["--no-dry-run"]):
result = parser.parse_args(supplied)
print(repr(supplied), "=>", result.dry_run)
In this code, when nothing is specified and--dry-runisTrue、--no-dry-runisFalse. The executor can disable the dry run--no-dry-runusing a clear option. However, for tools that permit production deletion or transmission, disabling confirmation mode alone does not mean the operation can be initiated. The design must separately verify target IDs, counts, permissions, and required approvals.
Visualizing what is treated as a boolean
The diagram below showstype=booland the difference between positive and negative flags. On the left, the input string isbool()sent to. On the right, the option name itself determines the boolean value. Do not confuse string words with the meaning of the option.
flowchart TD
A[引数を受け取る] --> B{指定方法を選ぶ}
B -- type=bool --> C[入力文字列をboolへ渡す]
C --> D[空でない文字列はTrue]
B -- BooleanOptionalAction --> E{オプション名を確認}
E -- 肯定形 --> F[True]
E -- 否定形 --> G[False]
type=boolinFalseWhen entering an appearance like--no-dry-runultimately passes a "non-empty string." In contrast,
is a name expressing the intent to "disable confirmation mode." What the diagram illustrates is not which is technically superior, but rather the difference in design that preserves the user's input intent.
Changing conditions: Defaults are not always safedefault=TrueThe above example is--verbose. Because this is the default when no confirmation mode is specified, it makes it harder to proceed with modification processing even if the user forgets to write the option. On the other hand,
for detailed display flags like--delete, it is natural to default to false and enable them only when necessary. Ifis designed to perform deletions when true, a default value of false is appropriate. In other words,there is no general rule that true means safe and false means dangerous--dry-run. You must verify the meaning represented by the flag name in conjunction with the program branching. Even if the name alone is changed to
In actual operations, you also design the conditions that allow disabling check mode, the target scope, and how to roll back in case of an error. The correctness of a single parse result is not the same as the safety of the entire command. Before integrating into production, verify both normal execution and negative specifications using dummy data.
Troubleshooting failures: Check three places
First,repr()Check the argument strings received by the program using tools such as these. Shell-side quoting or environment variable expansion may cause unintended strings to be passed. Next,argparseCheck the type and value of the parse result. Finally, examine the conditional branch in the logic reading that boolean value. Failing to distinguish among these three leads to confusing argument conversion errors with implementation errors in deletion or transmission processing.
BooleanOptionalActionIf it indicates that does not exist, check whether the Python version is lower than 3.9. In that case,store_trueandstore_falsecan be assigned to separate options while storing them in the same destination variable. However, note that if both are specified simultaneously, you must determine their precedence and clearly state it in the help text, otherwise it will introduce another ambiguity.
--dry-run FalseIf results in an error as an extraneous argument,store_trueorBooleanOptionalActionmight be operating as valueless flags. In this case, do not append the stringFalse--no-dry-run, and use the negative form if necessary. Do not pass simply because 'the error is gone'; verify that the expected boolean value was obtained.
How to revise operational manuals and monitoring logs
In teams operating CLIs, concrete examples of arguments are recorded in manuals and job definitions. If you only fix the implementation and leave old examples like--dry-run False, the next person in charge will make the same misconception. First, reproduce the issue with dummy input, rewrite the correct options after the fix, and explain the behavioral changes caused by those arguments.
In logs, avoiding sensitive or personal information while recording the necessary scope of the actually interpreted check mode value makes root cause analysis easier. Especially when hazardous processing is involved, displaying what was intended to be targeted before making changes helps detect configuration errors early. However, writing logs does not guarantee the safety of operations.
The operational conclusion is to avoid carelessly using in commands handling boolean values, and instead design the presence of flags and their affirmative or negative forms to clearly represent the user's operational intent.type=bool Confirm through experimentation that correct values are entered, and maintain operational safety measures at other layers as well.
Official documentation and verification scope
Official Python Documentation: argparse —
store_true、BooleanOptionalAction、parse_argsspecificationOfficial Python Documentation: Truth Value Testing — Truth values of empty and non-empty strings
In this local verification, we executed string conversions betweenFalse and 0, along with affirmation, negation, and omission. This was not applied to CLI tools that overwrite actual files or to production jobs.
