Skip to content

Parser

parser

Construct the command-line parser and its three subcommands.

Parser construction is intentionally separate from runtime resolution. This module describes syntax and performs scalar validation; create_command_config() resolves files, environment configuration, and domain objects after parsing succeeds.

create_argument_parser

create_argument_parser() -> ArgumentParser

Create the complete top-level argument parser.

The parser exposes three independent workflows: typo generation, AutoCorrect2 conflict checking, and their composed full pipeline. Shared options are added by private helpers so their spelling and validation stay consistent across subcommands.

Returns:

Type Description
ArgumentParser

Configured parser ready to parse a command-line argument sequence.

Source code in hotstring\cli\parser.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def create_argument_parser() -> argparse.ArgumentParser:
    """Create the complete top-level argument parser.

    The parser exposes three independent workflows: typo generation,
    AutoCorrect2 conflict checking, and their composed full pipeline. Shared
    options are added by private helpers so their spelling and validation stay
    consistent across subcommands.

    Returns:
        Configured parser ready to parse a command-line argument sequence.
    """
    parser = argparse.ArgumentParser(description=_PROGRAM_DESCRIPTION)
    parser.add_argument(
        "-v",
        "--verbose",
        dest="verbosity",
        action="count",
        default=0,
        help="increase logging detail; repeat for debug output",
    )

    subparsers = parser.add_subparsers(
        dest="command",
        metavar="COMMAND",
        required=True,
    )

    typo_generation = subparsers.add_parser(
        "typo-generation",
        help="generate and internally validate typo mappings",
        description="Generate typo mappings without inspecting AutoCorrect2.",
    )
    _add_word_source_arguments(typo_generation)
    _add_generation_arguments(typo_generation)
    _add_report_argument(typo_generation)

    autocorrect2_check = subparsers.add_parser(
        "autocorrect2-check",
        help="check supplied candidates against AutoCorrect2",
        description=(
            "Check explicitly supplied candidate hotstrings without running typo generation."
        ),
    )
    _add_candidate_source_arguments(autocorrect2_check)
    _add_autocorrect2_arguments(autocorrect2_check)
    _add_report_argument(autocorrect2_check)

    full_pipeline = subparsers.add_parser(
        "full-pipeline",
        help="generate typos and check them against AutoCorrect2",
        description=(
            "Generate typo candidates, remove internal ambiguity, and check "
            "the survivors against AutoCorrect2."
        ),
    )
    _add_word_source_arguments(full_pipeline)
    _add_generation_arguments(full_pipeline)
    _add_autocorrect2_arguments(full_pipeline)
    _add_report_argument(full_pipeline)

    return parser

_add_word_source_arguments

_add_word_source_arguments(parser: ArgumentParser) -> None

Add the mutually exclusive source-word inputs.

Parameters:

Name Type Description Default
parser ArgumentParser

Subparser receiving the arguments.

required
Source code in hotstring\cli\parser.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def _add_word_source_arguments(parser: argparse.ArgumentParser) -> None:
    """Add the mutually exclusive source-word inputs.

    Args:
        parser:
            Subparser receiving the arguments.
    """
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "--word",
        action="append",
        metavar="WORD",
        help="source word to process; repeat to supply multiple words",
    )
    group.add_argument(
        "--words-file",
        type=Path,
        metavar="PATH",
        help="UTF-8 text file containing one source word per line",
    )

_add_candidate_source_arguments

_add_candidate_source_arguments(
    parser: ArgumentParser,
) -> None

Add the mutually exclusive candidate inputs.

Parameters:

Name Type Description Default
parser ArgumentParser

Subparser receiving the arguments.

required
Source code in hotstring\cli\parser.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
def _add_candidate_source_arguments(parser: argparse.ArgumentParser) -> None:
    """Add the mutually exclusive candidate inputs.

    Args:
        parser:
            Subparser receiving the arguments.
    """
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "--candidate",
        action="append",
        nargs=3,
        metavar=("TRIGGER", "REPLACEMENT", "OPTIONS"),
        help=(
            "candidate semantic trigger, replacement, and AHK option string; "
            "repeat to supply multiple candidates"
        ),
    )
    group.add_argument(
        "--candidates-file",
        type=Path,
        metavar="PATH",
        help="UTF-8 JSON file containing candidate objects",
    )

_add_generation_arguments

_add_generation_arguments(parser: ArgumentParser) -> None

Add settings shared by typo-generating commands.

Parameters:

Name Type Description Default
parser ArgumentParser

Subparser receiving the arguments.

required
Source code in hotstring\cli\parser.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def _add_generation_arguments(parser: argparse.ArgumentParser) -> None:
    """Add settings shared by typo-generating commands.

    Args:
        parser:
            Subparser receiving the arguments.
    """
    parser.add_argument(
        "--single-attempts",
        dest="single_error_attempts_per_word",
        type=_positive_integer,
        required=True,
        metavar="COUNT",
        help="attempts per word for each forced single-error task",
    )
    parser.add_argument(
        "--multi-attempts",
        dest="multi_error_attempts_per_word",
        type=_positive_integer,
        required=True,
        metavar="COUNT",
        help="attempts per word for the mixed two-error task",
    )
    parser.add_argument(
        "--multi-min-length",
        dest="multi_error_minimum_word_length",
        type=_minimum_two_integer,
        required=True,
        metavar="LENGTH",
        help="minimum word length eligible for the mixed two-error task",
    )
    parser.add_argument(
        "--language",
        type=_nonempty_text,
        default="english",
        metavar="LANGUAGE",
        help="MULTYPO language identifier (default: %(default)s)",
    )
    parser.add_argument(
        "--excluding-set",
        action=argparse.BooleanOptionalAction,
        default=True,
        help="enable or disable MULTYPO's language excluding set",
    )
    parser.add_argument(
        "--keyboard-weights",
        dest="keyboard_neighbor_weights",
        type=_positive_number,
        nargs=2,
        default=(9.0, 1.0),
        metavar=("HORIZONTAL", "VERTICAL"),
        help="relative keyboard-neighbor weights (default: 9 1)",
    )
    parser.add_argument(
        "--workers",
        type=_positive_integer,
        metavar="COUNT",
        help="process-pool size; omit to let the executor choose",
    )

_add_autocorrect2_arguments

_add_autocorrect2_arguments(parser: ArgumentParser) -> None

Add settings shared by AutoCorrect2-aware commands.

Parameters:

Name Type Description Default
parser ArgumentParser

Subparser receiving the arguments.

required
Source code in hotstring\cli\parser.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def _add_autocorrect2_arguments(parser: argparse.ArgumentParser) -> None:
    """Add settings shared by AutoCorrect2-aware commands.

    Args:
        parser:
            Subparser receiving the arguments.
    """
    parser.add_argument(
        "--project-dir",
        dest="autocorrect2_project_dir",
        type=Path,
        metavar="PATH",
        help=(
            "AutoCorrect2 project directory; takes precedence over environment "
            "and .env configuration"
        ),
    )
    parser.add_argument(
        "--env-file",
        type=Path,
        metavar="PATH",
        help=(
            f"dotenv file containing {AUTOCORRECT2_PROJECT_DIR_ENV}; used after "
            "the process environment (default fallback: project-root .env)"
        ),
    )
    parser.add_argument(
        "--write-accepted",
        action="store_true",
        help="append accepted candidates to AutoCorrect2's generated include",
    )

_add_report_argument

_add_report_argument(parser: ArgumentParser) -> None

Add the optional report destination.

Parameters:

Name Type Description Default
parser ArgumentParser

Subparser receiving the argument.

required
Source code in hotstring\cli\parser.py
227
228
229
230
231
232
233
234
235
236
237
238
239
def _add_report_argument(parser: argparse.ArgumentParser) -> None:
    """Add the optional report destination.

    Args:
        parser:
            Subparser receiving the argument.
    """
    parser.add_argument(
        "--report",
        type=Path,
        metavar="PATH",
        help="write the workflow report to this path",
    )

_positive_integer

_positive_integer(value: str) -> int

Parse a strictly positive integer for argparse.

Parameters:

Name Type Description Default
value str

Raw argument text.

required

Returns:

Type Description
int

Parsed positive integer.

Raises:

Type Description
ArgumentTypeError

If the value is not an integer greater than zero.

Source code in hotstring\cli\parser.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def _positive_integer(value: str) -> int:
    """Parse a strictly positive integer for `argparse`.

    Args:
        value:
            Raw argument text.

    Returns:
        Parsed positive integer.

    Raises:
        argparse.ArgumentTypeError:
            If the value is not an integer greater than zero.
    """
    try:
        parsed = int(value)
    except ValueError as exc:
        raise argparse.ArgumentTypeError("must be an integer") from exc
    if parsed <= 0:
        raise argparse.ArgumentTypeError("must be greater than zero")
    return parsed

_minimum_two_integer

_minimum_two_integer(value: str) -> int

Parse an integer greater than or equal to two.

Parameters:

Name Type Description Default
value str

Raw argument text.

required

Returns:

Type Description
int

Parsed integer.

Raises:

Type Description
ArgumentTypeError

If the value is not an integer of at least two.

Source code in hotstring\cli\parser.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def _minimum_two_integer(value: str) -> int:
    """Parse an integer greater than or equal to two.

    Args:
        value:
            Raw argument text.

    Returns:
        Parsed integer.

    Raises:
        argparse.ArgumentTypeError:
            If the value is not an integer of at least two.
    """
    parsed = _positive_integer(value)
    if parsed < 2:
        raise argparse.ArgumentTypeError("must be at least 2")
    return parsed

_positive_number

_positive_number(value: str) -> float

Parse a strictly positive floating-point value.

Parameters:

Name Type Description Default
value str

Raw argument text.

required

Returns:

Type Description
float

Parsed positive number.

Raises:

Type Description
ArgumentTypeError

If the value is not finite and greater than zero.

Source code in hotstring\cli\parser.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def _positive_number(value: str) -> float:
    """Parse a strictly positive floating-point value.

    Args:
        value:
            Raw argument text.

    Returns:
        Parsed positive number.

    Raises:
        argparse.ArgumentTypeError:
            If the value is not finite and greater than zero.
    """
    try:
        parsed = float(value)
    except ValueError as exc:
        raise argparse.ArgumentTypeError("must be a number") from exc
    if parsed <= 0 or not parsed < float("inf"):
        raise argparse.ArgumentTypeError("must be a finite number greater than zero")
    return parsed

_nonempty_text

_nonempty_text(value: str) -> str

Reject an empty or whitespace-only text argument.

Parameters:

Name Type Description Default
value str

Raw argument text.

required

Returns:

Type Description
str

Trimmed text.

Raises:

Type Description
ArgumentTypeError

If the value contains no non-whitespace characters.

Source code in hotstring\cli\parser.py
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
def _nonempty_text(value: str) -> str:
    """Reject an empty or whitespace-only text argument.

    Args:
        value:
            Raw argument text.

    Returns:
        Trimmed text.

    Raises:
        argparse.ArgumentTypeError:
            If the value contains no non-whitespace characters.
    """
    parsed = value.strip()
    if not parsed:
        raise argparse.ArgumentTypeError("must not be empty")
    return parsed