Skip to content

Commit

Permalink
added option to forget during clean (#220)
Browse files Browse the repository at this point in the history
* extended clean with option --hard (git like) and adapted existing tests

* made --hard effective by forgetting task being cleaned

* tested hard clean

* added clean --hard to docs

* added support for `clean_hard` configuration option

* adapted changelog

* always closing dependency manager, AUTHORS

* renamed hard --> clean-forget, removed superfluous config param

* adapted docs

* renamed long command option to not repeat "clean"

* adapted docs

* adapted test names
  • Loading branch information
moltob authored and schettino72 committed Nov 11, 2017
1 parent 1a1c717 commit 1f195df
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 21 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
* Gerlad Storer - https://github.com/gstorer
* Simon Mutch - https://github.com/smutch
* Michael Milton - https://github.com/tmiguelt
* Mike Pagel - https://github.com/moltob
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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 `--forget` can be used to also forget about cleaned tasks


0.30.3 (*2017-02-20*)
Expand Down
9 changes: 9 additions & 0 deletions doc/cmd_other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ 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
*--forget*. This can be made the default behavior by adding the corresponding ``cleanforget``
configuration switch:

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

opt_clean_forget = {
'name': 'cleanforget',
'long': 'forget',
'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_forget)


def clean_tasks(self, tasks, dryrun):
def clean_tasks(self, tasks, dryrun, cleanforget):
"""ensure task clean-action is executed only once"""
cleaned = set()
forget_tasks = cleanforget 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)

self.dep_manager.close()

def _execute(self, dryrun, cleandep, cleanall, pos_args=None):
def _execute(self, dryrun, cleandep, cleanall, cleanforget, pos_args=None):
"""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 cleanforget (bool): forget cleaned tasks
@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 +103,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, cleanforget)
65 changes: 48 additions & 17 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 @@ -24,60 +25,90 @@ 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 = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock.MagicMock())
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)
sel_tasks=['t1'], dep_manager=mock.MagicMock())
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 = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock.MagicMock())
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 = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock.MagicMock())
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 = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock.MagicMock())
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 = CmdFactory(Clean, outstream=output, task_list=tasks,
dep_manager=mock.MagicMock())
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_forget_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_forget_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 1f195df

Please sign in to comment.