pydoit / doit

CLI task management & automation tool
http://pydoit.org
MIT License
1.87k stars 175 forks source link

normalize_callable breaking action property for positional arguments #441

Open mlkoenig opened 2 years ago

mlkoenig commented 2 years ago

Defining a CmdAction with a positional argument leads to untestable task testing. For instance if we use the following minimal task

from doit.action import CmdAction

def task_pos_args():

    def show_params(pos):
        return f"echo {pos}"

    return {
        'actions':[CmdAction(show_params)],
        'pos_arg': 'pos',
        'verbosity': 2,
    }

and write a test that calls the action with parametrized arguments

import pytest

@pytest.mark.parametrize("pos", ["arg", ["arg1", "arg2"]])
def test_pos_args(pos):
    cmd_action = task_pos_args()["action"][0]
    cmd = cmd_action.action().format(pos=pos)
    assert cmd == f"echo {pos}"

will fail, because calling the action property on a CmdAction with a positional argument throws the error missing 1 positional argument. https://github.com/pydoit/doit/blob/83309d81a7eb6dda30b9d04a05dd99a2de44192b/doit/action.py#L148-L156 Looking at the above show_params is called as ref on empty args field due to normalize_callable not returning the correct signature. https://github.com/pydoit/doit/blob/83309d81a7eb6dda30b9d04a05dd99a2de44192b/doit/action.py#L17-L23

Environment

  1. OS: Linux
  2. python version: 3.9.15
  3. doit version: 0.36.0

Edit: added imports to minimal examples

Fund with Polar

mlkoenig commented 2 years ago

As a temporary solution one can skip the action property entirely and call the underlying _action directly, e.g.

import pytest

@pytest.mark.parametrize("pos", ["arg", ["arg1", "arg2"]])
def test_pos_args(pos):
    cmd_action = task_pos_args()["action"][0]
    cmd = cmd_action._action(pos)
    assert cmd == f"echo {pos}"