planemo.autopygen.commands.command_utils — Planemo 0.75.31.dev0 documentation (original) (raw)

""" Module used for creation and manipulation of template elements are parts of template, separated from the rest by comments with specific structure Example of the comments:

## foo definition
... block itself ...
## end foo definition

Elements can be nested """

from typing import List

from planemo.autopygen.param_info import ( ParamDataType, ParamInfo, )

SPACE = " " DEFAULT_INDENT = 4 ADD_COMMENTS_BY_DEFAULT = False

[docs] class DefinitionNotFoundException(Exception): """ Exception raised if part of template definition cannot be found """

pass

[docs] def create_flag( variable: str, comment: str, depth: int, indent=DEFAULT_INDENT, add_comment: bool = ADD_COMMENTS_BY_DEFAULT ) -> str: """ Function used to create a flag definition, wrapped in a comment

Parameters
----------
variable : str
    name of variable, containing $ at the beginning
comment : str
    wrapping comment
depth : int
  integer, used to set the depth of the current element.
  This value is used to indent the block properly
indent : int
  default value for size of the block indent
add_comment : bool
  option that enables or disables formatting comments
"""
result = f"{depth * indent * SPACE}{variable}\n"

if not add_comment:
    return result

return f"{depth * indent * SPACE}## FLAG {comment}\n" f"{result}" f"{depth * indent * SPACE}## end FLAG {comment}\n"

[docs] def create_element_with_body( kind: str, head: str, body: List[str], comment: str, depth: int, indent: int = DEFAULT_INDENT, body_indented: bool = True, add_comment: bool = ADD_COMMENTS_BY_DEFAULT, ) -> str: """ Function used to create block of template, like if or loop

Parameters
----------
kind : str
  string defining what kind of element is created, for example if or for
  (loop)
head : str
  body of block header, for example predicate of condition, or the
  body of loop
body : str
  body of the block, can be another element
comment : str
  comment, used to set the start and end of the block
depth : int
  integer, used to set the depth of the current element.
  This value is used to indent the block properly
indent : int
  default value for size of the block indent
body_indented : bool
  option that define whether body is already correctly indented
add_comment : bool
  option that enables or disables formatting comments

Returns
-------
string containing the created template element
"""
result = []
if add_comment:
    result.append(f"{depth * indent * SPACE}## {comment}\n")

result.append(f"{depth * indent * SPACE}#{kind} {head}:\n")

body_indent = ""
if body:
    if not body_indented:
        body_indent = (depth + 1) * indent * SPACE
        body[0] = f"{body_indent}{body[0]}"

    translated_body = ("\n" + body_indent).join(body)
    if translated_body[-1] != "\n":
        translated_body += "\n"

    result.append(translated_body)

result.append(f"{depth * indent * SPACE}#end {kind}\n")

if add_comment:
    result += f"{depth * indent * SPACE}## end {comment}\n"

return "".join(result)

[docs] def transform_param_info(info: ParamInfo, namespace: str, depth: int): if info.param_type.is_help or info.param_type.is_version: raise ParamTypeNotSupported("Transformation for these param types are not supported")

name = info.name
separator = "." if namespace else ""
variable = f"${namespace}{separator}{name}"
if not info.param_type.is_repeat:
    if info.param_type.is_flag:
        return create_flag(variable, f"{name} definition", depth)
    else:
        body_expression = create_body_expression(info, variable, depth + 1)
        return create_element_with_body("if", variable, [body_expression], f"{name} definition", depth)

iteration_var = "$item"
if info.param_type.is_flag:
    param = create_flag(iteration_var, f"{name} definition", depth + 1)
else:
    body_expression = create_body_expression(info, iteration_var, depth + 2)
    param = create_element_with_body("if", iteration_var, [body_expression], f"{name} definition", depth + 1)

head_expression = f"{iteration_var} in ${namespace}{separator}{info.name}"

return create_element_with_body("for", head_expression, [param], f"{info.name} definition", depth)

TODO generating command like this assumes that parameters are added to argparse in the right order.

Separating arguments into positional and non-positional is necessary,

otherwise the command generator will not work correctly

[docs] def create_body_expression(info: ParamInfo, variable: str, depth: int, indentation: int = DEFAULT_INDENT) -> str: stripped_arg = info.argument.lstrip("-") str_indent = SPACE * depth * indentation

wrapped_variable = variable
if info.type == ParamDataType.DATA or info.type == ParamDataType.TEXT:
    wrapped_variable = f"'{wrapped_variable}'"

if stripped_arg == info.argument:
    return f"{str_indent}{variable}"
return f"{str_indent}{info.argument} {wrapped_variable}"

[docs] class ParamTypeNotSupported(Exception): pass