Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions moulinette/interfaces/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ def parse_accept_language(header: str) -> list[tuple[str, str | None]]:
return m18n.default_locale


# FIXME This parser transform API call into CLI command and parse
# it with argparse. There are probably method to validate and ordering
# API arguments without relying on argparse cli tools
class _HTTPArgumentParser:
"""Argument parser for HTTP requests

Expand Down Expand Up @@ -227,21 +230,31 @@ def append(arg_strings, value, action):
value.save(UPLOAD_DIR)
if option_string is not None:
arg_strings.append(option_string)
arg_strings.append(UPLOAD_DIR + "/" + value.filename)
arg_strings.append(f"{UPLOAD_DIR}/{value.filename}")
elif isinstance(value, str):
# ==== SPACE DIRTY_HACK =====
# In order to avoid option injection by starting a value with a
# prefix_chars (here @), we add a starting space on string values
# starting with @, like that argparse do not interpret it as an
# option...
protected_value = f" {value}" if value.startswith("@") else value
if option_string is not None:
arg_strings.append(option_string)
# TODO: Review this fix
# FIXME What if value is an empty string ?
if value:
arg_strings.append(value)
arg_strings.append(protected_value)
else:
arg_strings.append(value)
arg_strings.append(protected_value)
elif isinstance(value, list):
if option_string is not None:
arg_strings.append(option_string)
for v in value:
if isinstance(v, str):
arg_strings.append(v)
# Same space dirty hack as above

protected_value = f" {v}" if v.startswith("@") else v
arg_strings.append(protected_value)
else:
logger.warning(
"unsupported argument value type %r "
Expand All @@ -260,16 +273,30 @@ def append(arg_strings, value, action):
return arg_strings

# Iterate over positional arguments
known_args = []
for action in self._positional:
if action.dest in args:
arg_strings = append(arg_strings, args[action.dest], action)
known_args.append((action.dest, args[action.dest]))

# Iterate over optional arguments
for dest, action in self._optional.items():
if dest in args:
arg_strings = append(arg_strings, args[dest], action)
known_args.append((dest, args[dest]))

parsed_args = self._parser.parse_args(arg_strings, namespace)

return self._parser.parse_args(arg_strings, namespace)
# Post-treatment of the dirty hack describe above
parsed_args_dict = vars(parsed_args)
for option_string, value in known_args:
if option_string not in args:
continue
protected_value = parsed_args_dict[option_string]
if isinstance(protected_value, str) and value.startswith("@"):
# We remove the starting space we have added on values starting with @...
setattr(parsed_args, option_string, protected_value[1:])
return parsed_args

def _error(self, message):
raise MoulinetteValidationError(message, raw_msg=True)
Expand Down
Loading