Skip to content

Commit 8b366f5

Browse files
committed
a slight refactoring
- slightly edit error messages
1 parent 223ae0c commit 8b366f5

File tree

2 files changed

+51
-29
lines changed

2 files changed

+51
-29
lines changed

click_option_group/_core.py

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -270,17 +270,23 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: d
270270
return
271271

272272
if all(o.hidden for o in self.get_options(ctx).values()):
273-
error_text = (f'Need at least one non-hidden option in RequiredAnyOptionGroup '
274-
f'"{self.get_default_name(ctx)}".')
275-
raise TypeError(error_text)
273+
cls_name = self.__class__.__name__
274+
group_name = self.get_default_name(ctx)
275+
276+
raise TypeError(
277+
f"Need at least one non-hidden option in group '{group_name}' ('{cls_name}')."
278+
)
276279

277280
option_names = set(self.get_options(ctx))
278281

279282
if not option_names.intersection(opts):
280-
error_text = f'Missing one of the required options from "{self.get_default_name(ctx)}" option group:'
281-
error_text += f'\n{self.get_error_hint(ctx)}'
283+
group_name = self.get_default_name(ctx)
284+
option_info = self.get_error_hint(ctx)
282285

283-
raise click.UsageError(error_text, ctx=ctx)
286+
raise click.UsageError(
287+
f"At least one of the following options from '{group_name}' group is required:\n{option_info}",
288+
ctx=ctx
289+
)
284290

285291

286292
class RequiredAllOptionGroup(OptionGroup):
@@ -301,12 +307,14 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: d
301307
option_names = set(self.get_options(ctx))
302308

303309
if not option_names.issubset(opts):
310+
group_name = self.get_default_name(ctx)
304311
required_names = option_names.difference(option_names.intersection(opts))
312+
option_info = self.get_error_hint(ctx, required_names)
305313

306-
error_text = f'Missing required options from "{self.get_default_name(ctx)}" option group:'
307-
error_text += f'\n{self.get_error_hint(ctx, required_names)}'
308-
309-
raise click.UsageError(error_text, ctx=ctx)
314+
raise click.UsageError(
315+
f"Missing required options from '{group_name}' group:\n{option_info}",
316+
ctx=ctx
317+
)
310318

311319

312320
class MutuallyExclusiveOptionGroup(OptionGroup):
@@ -330,9 +338,14 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: d
330338
given_option_count = len(given_option_names)
331339

332340
if given_option_count > 1:
333-
error_text = 'The given mutually exclusive options cannot be used at the same time:'
334-
error_text += f'\n{self.get_error_hint(ctx, given_option_names)}'
335-
raise click.UsageError(error_text, ctx=ctx)
341+
group_name = self.get_default_name(ctx)
342+
option_info = self.get_error_hint(ctx, given_option_names)
343+
344+
raise click.UsageError(
345+
f"Mutually exclusive options from '{group_name}' group "
346+
f"cannot be used at the same time:\n{option_info}",
347+
ctx=ctx
348+
)
336349

337350

338351
class RequiredMutuallyExclusiveOptionGroup(MutuallyExclusiveOptionGroup):
@@ -353,17 +366,21 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: d
353366
given_option_names = option_names.intersection(opts)
354367

355368
if len(given_option_names) == 0:
356-
error_text = ('Missing one of the required mutually exclusive options from '
357-
f'"{self.get_default_name(ctx)}" option group:')
358-
error_text += f'\n{self.get_error_hint(ctx)}'
359-
raise click.UsageError(error_text, ctx=ctx)
369+
group_name = self.get_default_name(ctx)
370+
option_info = self.get_error_hint(ctx)
371+
372+
raise click.UsageError(
373+
"Missing one of the required mutually exclusive options from "
374+
f"'{group_name}' option group:\n{option_info}",
375+
ctx=ctx
376+
)
360377

361378

362379
class AllOptionGroup(OptionGroup):
363380
"""Option group with required all/none options of this group
364381
365382
`AllOptionGroup` defines the behavior:
366-
- All options from the group must be set or None must be set.
383+
- All options from the group must be set or None must be set
367384
"""
368385

369386
@property
@@ -378,9 +395,11 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: d
378395
option_names = set(self.get_options(ctx))
379396

380397
if not option_names.isdisjoint(opts) and option_names.intersection(opts) != option_names:
381-
error_text = f'All options should be specified or None should be specified from the group ' \
382-
f'"{self.get_default_name(ctx)}".'
383-
error_text += f'\nMissing required options from "{self.get_default_name(ctx)}" option group.'
384-
error_text += f'\n{self.get_error_hint(ctx)}'
385-
error_text += '\n'
386-
raise click.UsageError(error_text, ctx=ctx)
398+
group_name = self.get_default_name(ctx)
399+
option_info = self.get_error_hint(ctx)
400+
401+
raise click.UsageError(
402+
"All options should be specified or none should be specified from the group "
403+
f"'{group_name}'. Missing required options:\n{option_info}",
404+
ctx=ctx
405+
)

tests/test_click_option_group.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def cli(foo, bar):
263263
result = runner.invoke(cli, [])
264264
assert result.exception
265265
assert result.exit_code == 2
266-
assert 'Missing one of the required options' in result.output
266+
assert 'At least one of the following options' in result.output
267267
assert '--foo' in result.output
268268
assert '--bar' in result.output
269269

@@ -303,7 +303,7 @@ def cli(foo, bar):
303303
result = runner.invoke(cli, ['--foo', 'foo'])
304304
assert result.exception
305305
assert result.exit_code == 2
306-
assert 'All options should be specified or None should be specified from the group' in result.output
306+
assert 'All options should be specified or none should be specified' in result.output
307307
assert '--foo' in result.output
308308
assert '--bar' in result.output
309309

@@ -377,21 +377,24 @@ def cli(foo, bar, spam):
377377
result = runner.invoke(cli, ['--foo', 'foo', '--bar', 'bar'])
378378
assert result.exception
379379
assert result.exit_code == 2
380-
assert 'The given mutually exclusive options cannot be used at the same time' in result.output
380+
assert 'Mutually exclusive options from' in result.output
381+
assert 'cannot be used at the same time' in result.output
381382
assert '--foo' in result.output
382383
assert '--bar' in result.output
383384

384385
result = runner.invoke(cli, ['--foo', 'foo', '--spam', 'spam'])
385386
assert result.exception
386387
assert result.exit_code == 2
387-
assert 'The given mutually exclusive options cannot be used at the same time' in result.output
388+
assert 'Mutually exclusive options from' in result.output
389+
assert 'cannot be used at the same time' in result.output
388390
assert '--foo' in result.output
389391
assert '--spam' in result.output
390392

391393
result = runner.invoke(cli, ['--bar', 'bar', '--spam', 'spam'])
392394
assert result.exception
393395
assert result.exit_code == 2
394-
assert 'The given mutually exclusive options cannot be used at the same time' in result.output
396+
assert 'Mutually exclusive options from' in result.output
397+
assert 'cannot be used at the same time' in result.output
395398
assert '--bar' in result.output
396399
assert '--spam' in result.output
397400

0 commit comments

Comments
 (0)