Skip to content

Commit

Permalink
Merge 4bb68cd into 8e7405d
Browse files Browse the repository at this point in the history
  • Loading branch information
moltob committed Nov 5, 2017
2 parents 8e7405d + 4bb68cd commit 5f903f4
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 15 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Changes
- CmdParse now support getting values from OS environment variables
- option `seek_file` control by ENV var `DOIT_SEEK_FILE`
- GH-#192 ipython extension uses `load_ipython_extension`
- GH-#218 clean with option `--hard` or configuration option `clean_hard` can be used to also
forget about cleaned tasks


0.30.3 (*2017-02-20*)
Expand Down
8 changes: 8 additions & 0 deletions doc/cmd_other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ If you are executing the default tasks this flag is automatically set.

If you want check which tasks the clean operation would affect you can use the option *-n*/*--dry-run*.

If you like to also make doit forget previous execution of cleaned tasks, use option *--hard*. This
can be made the default behavior by adding the corresponding ``clean_hard`` configuration switch:

.. code-block:: python
DOIT_CONFIG = {
'clean_hard': True,
}
ignore
Expand Down
22 changes: 18 additions & 4 deletions doit/cmd_clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,46 @@
'help': 'clean all task',
}

opt_clean_hard = {
'name': 'hard',
'long': 'hard',
'type': bool,
'default': False,
'help': 'also forget tasks after cleaning',
}

class Clean(DoitCmdBase):
doc_purpose = "clean action / remove targets"
doc_usage = "[TASK ...]"
doc_description = ("If no task is specified clean default tasks and "
"set --clean-dep automatically.")

cmd_options = (opt_clean_cleandep, opt_clean_cleanall, opt_clean_dryrun)
cmd_options = (opt_clean_cleandep, opt_clean_cleanall, opt_clean_dryrun, opt_clean_hard)


def clean_tasks(self, tasks, dryrun):
def clean_tasks(self, tasks, dryrun, hard):
"""ensure task clean-action is executed only once"""
cleaned = set()
forget_tasks = hard and not dryrun
for task in tasks:
if task.name not in cleaned:
cleaned.add(task.name)
task.clean(self.outstream, dryrun)
if forget_tasks:
self.dep_manager.remove(task.name)

if forget_tasks:
self.dep_manager.close()

def _execute(self, dryrun, cleandep, cleanall, pos_args=None):
def _execute(self, dryrun, cleandep, cleanall, hard, pos_args=None, clean_hard=False):
"""Clean tasks
@param task_list (list - L{Task}): list of all tasks from dodo file
@ivar dryrun (bool): if True clean tasks are not executed
(just print out what would be executed)
@param cleandep (bool): execute clean from task_dep
@param cleanall (bool): clean all tasks
@param hard (bool): forget cleaned tasks (command line option)
@param clean_hard (bool): forget cleaned tasks (doit config parameter)
@var default_tasks (list - string): list of default tasks
@var selected_tasks (list - string): list of tasks selected
from cmd-line
Expand Down Expand Up @@ -91,4 +105,4 @@ def _execute(self, dryrun, cleandep, cleanall, pos_args=None):
to_clean.append(task)
to_clean.extend(subtasks_iter(tasks, task))
to_clean.reverse()
self.clean_tasks(to_clean, dryrun)
self.clean_tasks(to_clean, dryrun, hard or clean_hard)
48 changes: 37 additions & 11 deletions tests/test_cmd_clean.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from io import StringIO
import mock
import pytest

from doit.exceptions import InvalidCommand
Expand All @@ -25,59 +26,84 @@ def myclean(name):
def test_clean_all(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, False, True)
cmd_clean._execute(False, False, True, False)
assert ['t1','t2', 't3:a', 't3', 't4'] == self.cleaned

def test_clean_default(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
sel_tasks=['t1'])
cmd_clean._execute(False, False, False)
cmd_clean._execute(False, False, False, False)
# default enable --clean-dep by default
assert ['t2', 't1'] == self.cleaned

def test_clean_default_all(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, False, False)
cmd_clean._execute(False, False, False, False)
# default enable --clean-dep by default
assert set(['t1','t2', 't3:a', 't3', 't4']) == set(self.cleaned)

def test_clean_selected(self, tasks):
output = StringIO()
mock_dep_manager = mock.MagicMock()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
sel_tasks=['t1'])
cmd_clean._execute(False, False, False, ['t2'])
sel_tasks=['t1'], dep_manager=mock_dep_manager)
cmd_clean._execute(False, False, False, False, ['t2'])
assert ['t2'] == self.cleaned
mock_dep_manager.remove.assert_not_called()

def test_clean_taskdep(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, True, False, ['t1'])
mock_dep_manager = mock.MagicMock()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock_dep_manager)
cmd_clean._execute(False, True, False, False, ['t1'])
assert ['t2', 't1'] == self.cleaned
mock_dep_manager.remove.assert_not_called()

def test_clean_taskdep_recursive(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, True, False, ['t4'])
cmd_clean._execute(False, True, False, False, ['t4'])
assert ['t2', 't1', 't4'] == self.cleaned

def test_clean_subtasks(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, False, False, ['t3'])
cmd_clean._execute(False, False, False, False, ['t3'])
assert ['t3:a', 't3'] == self.cleaned

def test_clean_taskdep_once(self, tasks):
# do not execute clean operation more than once
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks)
cmd_clean._execute(False, True, False, ['t1', 't2'])
cmd_clean._execute(False, True, False, False, ['t1', 't2'])
assert ['t2', 't1'] == self.cleaned

def test_clean_invalid_task(self, tasks):
output = StringIO()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
sel_tasks=['t1'])
pytest.raises(InvalidCommand, cmd_clean._execute,
False, False, False, ['xxxx'])
False, False, False, False, ['xxxx'])

def test_clean_hard_selected(self, tasks):
output = StringIO()
mock_dep_manager = mock.MagicMock()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
sel_tasks=['t1'], dep_manager=mock_dep_manager)
cmd_clean._execute(False, False, False, True, ['t2'])
assert ['t2'] == self.cleaned
mock_dep_manager.assert_has_calls([mock.call.remove(mock.ANY), mock.call.close()]) # order
assert mock_dep_manager.remove.call_args_list == [mock.call('t2')] # exactly t2, not more

def test_clean_hard_taskdep(self, tasks):
output = StringIO()
mock_dep_manager = mock.MagicMock()
cmd_clean = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock_dep_manager)
cmd_clean._execute(False, True, False, True, ['t1'])
assert ['t2', 't1'] == self.cleaned
mock_dep_manager.assert_has_calls([mock.call.remove(mock.ANY), mock.call.close()]) # order
assert mock_dep_manager.remove.call_args_list == [mock.call('t2'), mock.call('t1')]

0 comments on commit 5f903f4

Please sign in to comment.