CmdAction with list does not set shell=False
scr-oath opened this issue · 5 comments
cmd-action state
If action is a list of strings and instances of any Path class from pathlib, by default it will be executed without the shell (Popen argument shell=False).
However, if you trace through it, the call to Popen is still shell=True.
dodo.py
#!/usr/bin/env python3
import logging
import doit
from doit.action import CmdAction
DOIT_CONFIG = {
'verbosity': 2,
'default_tasks': ['check_results']
}
def task_check_results():
def check_results(echo_as_list_in_CmdAction, echo_as_list_in_CmdAction_shell_False):
failed = False
logging.warning('echo_as_list_in_CmdAction = "%s"', echo_as_list_in_CmdAction)
if echo_as_list_in_CmdAction != 'foo bar':
failed = True
logging.warning('echo_as_list_in_CmdAction_shell_False = "%s"', echo_as_list_in_CmdAction_shell_False)
if echo_as_list_in_CmdAction_shell_False != 'foo bar':
failed = True
return not failed
return {
'actions': [check_results],
'getargs': {
'echo_as_list_in_CmdAction': ('echo_as_list_in_CmdAction', 'stdout'),
'echo_as_list_in_CmdAction_shell_False': ('echo_as_list_in_CmdAction_shell_False', 'stdout'),
},
'uptodate': [False],
}
def task_echo_as_list_in_CmdAction():
return {
'actions': [CmdAction(['echo', '-n', 'foo bar'], save_out='stdout')],
}
def task_echo_as_list_in_CmdAction_shell_False():
return {
'actions': [CmdAction(['echo', '-n', 'foo bar'], save_out='stdout', shell=False)],
}
def task_echo_as_list():
return {
'actions': [['echo', '-n', 'foo bar']],
}
if __name__ == '__main__':
doit.run(globals())
Environment
- OS: macOS Monterey 12.0.0
- python version: 3.9.5
- doit version: 0.34.0
doit
. echo_as_list_in_CmdAction_shell_False
foo bar. echo_as_list_in_CmdAction
. check_results
WARNING:root:echo_as_list_in_CmdAction = "
"
WARNING:root:echo_as_list_in_CmdAction_shell_False = "foo bar"
TaskFailed - taskid:check_results
Python Task failed: '<function task_check_results.<locals>.check_results at 0x7fa636f175e0>' returned False
Here's another way of showing it:
dodo.py
#!/usr/bin/env python3
import logging
from typing import Optional
import doit
from doit.action import CmdAction
from doit.task import Task
DOIT_CONFIG = {
'verbosity': 2,
}
class CheckShell:
def __init__(self, want_shell: bool = False):
self.want_shell = want_shell
self.task: Optional[Task] = None
def configure_task(self, task: Task):
self.task = task
task.actions[0].save_out = 'stdout'
def __call__(self) -> bool:
if self.want_shell != self.task.actions[0].shell:
logging.error(f'For task {self.task.name}: wanted {self.want_shell}, but first action was {self.task.actions[0].shell}')
return True
def task_echo_as_list_in_CmdAction():
return {
'actions': [CmdAction(['echo', '-n', 'foo bar'], save_out='stdout')],
'uptodate': [CheckShell(want_shell=False)],
}
def task_echo_as_list_in_CmdAction_shell_False():
return {
'actions': [CmdAction(['echo', '-n', 'foo bar'], save_out='stdout', shell=False)],
'uptodate': [CheckShell(want_shell=False)],
}
def task_echo_as_list():
return {
'actions': [['echo', '-n', 'foo bar']],
'uptodate': [CheckShell(want_shell=False)],
}
if __name__ == '__main__':
doit.run(globals())
Run
doit
ERROR:root:For task echo_as_list_in_CmdAction: wanted False, but first action was True
-- echo_as_list_in_CmdAction
-- echo_as_list_in_CmdAction_shell_False
-- echo_as_list
If action is a list of strings and instances of any Path class from pathlib, by default it will be executed without the shell (Popen argument shell=False).
This applies ONLY when you do not explicitly use the CmdAction
class.
With this interpretation your examples show correct behaviour, right?
Maybe the docs could be more clear about that.
Yep - I guess I figured it out, and yes, documentation would be very helpful to explicitly state that with a quick note that a constructed CmdAction
does not infer its parameters (i.e if you want shell set to True of false, you have to pass it yourself).
And yes, this does seem to work with that new understanding; thanks for considering!
Even places like https://pydoit.org/dependencies.html?highlight=configure_task#saving-computed-values may need to link to that gotcha - the switch from ['cmd', 'arg1']
to CmdAction(['cmd', 'arg1'], save_out='stdout')
may surprise and confuse folks when the behavior switches out from under them.