Skip to content

Commit

Permalink
turn max_complexity down to 12
Browse files Browse the repository at this point in the history
  • Loading branch information
skullydazed committed Jan 29, 2024
1 parent 24932b7 commit cd88c9c
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 71 deletions.
74 changes: 42 additions & 32 deletions milc/milc.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,46 @@ def find_config_file(self) -> Path:

return Path(filedir, filename).resolve()

def _handle_deprecated(self, arg_name: str, kwargs: Dict[str, Any]) -> None:
"""Called by self.argument: Mark an argument as deprecated, if necessary.
"""
if 'deprecated' in kwargs:
self._deprecated_arguments[arg_name] = kwargs['deprecated']
if kwargs['help']:
kwargs['help'] += f" [Deprecated]: {kwargs['deprecated']}"
else:
kwargs['help'] = f"[Deprecated]: {kwargs['deprecated']}"
del kwargs['deprecated']

def _handle_arg_parsing(self, config_name: str, arg_name: str, args: Sequence[Any], kwargs: Dict[str, Any]) -> None:
"""Called by self.argument: Parse this argument into the right datastructures.
"""
arg_strings = get_argument_strings(self._arg_parser, *args, **kwargs)

if kwargs.get('arg_only'):
if arg_name not in self.arg_only:
self.arg_only[arg_name] = []

self.arg_only[arg_name].append(config_name)
del kwargs['arg_only']
else:
if arg_name not in self.default_arguments:
self.default_arguments[config_name] = {}

self.default_arguments[config_name][arg_name] = kwargs.get('default')

if self.config[config_name][arg_name] is None:
self.config[config_name][arg_name] = kwargs.get('default')

if config_name not in self.args_passed:
self.args_passed[config_name] = {}

self.args_passed[config_name][arg_name] = False

for arg in arg_strings:
if _in_argv(arg):
self.args_passed[config_name][arg_name] = True

def argument(self, *args: Any, **kwargs: Any) -> Callable[..., Any]:
"""Decorator to call self.add_argument or self.<subcommand>.add_argument.
"""
Expand All @@ -364,39 +404,9 @@ def argument_function(handler: Callable[..., Any]) -> Callable[..., Any]:
config_name = handler.__name__
subcommand_name = config_name.replace("_", "-")
arg_name = get_argument_name(self._arg_parser, *args, **kwargs)
arg_strings = get_argument_strings(self._arg_parser, *args, **kwargs)

if 'deprecated' in kwargs:
self._deprecated_arguments[arg_name] = kwargs['deprecated']
if kwargs['help']:
kwargs['help'] += f" [Deprecated]: {kwargs['deprecated']}"
else:
kwargs['help'] = f"[Deprecated]: {kwargs['deprecated']}"
del kwargs['deprecated']

if kwargs.get('arg_only'):
if arg_name not in self.arg_only:
self.arg_only[arg_name] = []

self.arg_only[arg_name].append(handler.__name__)
del kwargs['arg_only']
else:
if arg_name not in self.default_arguments:
self.default_arguments[config_name] = {}

self.default_arguments[config_name][arg_name] = kwargs.get('default')

if self.config[config_name][arg_name] is None:
self.config[config_name][arg_name] = kwargs.get('default')

if config_name not in self.args_passed:
self.args_passed[config_name] = {}

self.args_passed[config_name][arg_name] = False

for arg in arg_strings:
if _in_argv(arg):
self.args_passed[config_name][arg_name] = True
self._handle_deprecated(arg_name, kwargs)
self._handle_arg_parsing(config_name, arg_name, args, kwargs)

if handler is self._entrypoint:
self.add_argument(*args, **kwargs)
Expand Down
92 changes: 54 additions & 38 deletions milc/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ def password(
return None


def _cast_answer(answer_type: Callable[[str], str], answer: str) -> Any:
"""Attempt to convert answer to answer_type.
"""
try:
return answer_type(answer)
except Exception as e:
cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e))
return None


def question(
prompt: str,
*args: Any,
Expand All @@ -130,7 +140,7 @@ def question(
answer_type: Callable[[str], str] = str,
validate: Optional[Callable[..., bool]] = None,
**kwargs: Any,
) -> Optional[str]:
) -> str | Any:
"""Allow the user to type in a free-form string to answer.
| Argument | Description |
Expand Down Expand Up @@ -158,17 +168,10 @@ def question(

elif confirm:
if yesno('Is the answer "%s" correct?', answer, default=True):
try:
return answer_type(answer)
except Exception as e:
cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e))
return None
return _cast_answer(answer_type, answer)

else:
try:
return answer_type(answer)
except Exception as e:
cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e))
return _cast_answer(answer_type, answer)

elif default is not None:
return default
Expand Down Expand Up @@ -217,35 +220,48 @@ def choice(
prompt = '%s[%s] ' % (prompt, default + 1)

while True:
# Prompt for an answer.
cli.echo(formatted_heading)

for i, option in enumerate(options, 1):
cli.echo('\t{fg_cyan}%d.{fg_reset} %s', i, option)
answer = _choice_get_answer(options, default, prompt, formatted_heading)

answer = input(format_ansi(prompt))
if answer:
if confirm and not yesno('Is the answer "%s" correct?', answer, default=True):
continue

# If the user types in one of the options exactly use that
if answer in options:
return answer

# Massage the answer into a valid integer
if answer == '' and default is not None:
answer_index = default
elif answer.isnumeric():
answer_index = int(answer) - 1
else:
cli.log.error('Invalid choice: %s', answer)
cli.log.debug('Could not convert %s to int', answer)
continue

# Validate the answer
if answer_index >= len(options) or answer_index < 0:
cli.log.error('Invalid choice: %s', answer_index + 1)
continue

if confirm and not yesno('Is the answer "%s" correct?', answer_index + 1, default=True):
continue

# Return the answer they chose.
return options[answer_index]

def _choice_get_answer(
options: Sequence[str],
default: Optional[int],
prompt: str,
formatted_heading: str,
) -> Optional[str]:
"""Get an answer from the user for choice().
"""
# Prompt for an answer.
cli.echo(formatted_heading)

for i, option in enumerate(options, 1):
cli.echo('\t{fg_cyan}%d.{fg_reset} %s', i, option)

answer = input(format_ansi(prompt))

# If the user types in one of the options exactly use that
if answer in options:
return answer

# Massage the answer into a valid integer
if answer == '' and default is not None:
answer_index = default
elif answer.isnumeric():
answer_index = int(answer) - 1
else:
cli.log.error('Invalid choice: %s', answer)
return None

# Validate the answer
if answer_index >= len(options) or answer_index < 0:
cli.log.error('Invalid choice: %s', answer_index + 1)
return None

# Return the answer they chose.
return options[answer_index]
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ ignore =
per_file_ignores =
**/__init__.py:F401
tests/**:N802
max_complexity = 14
max_complexity = 12

[metadata]
author = skullydazed
Expand Down

0 comments on commit cd88c9c

Please sign in to comment.