Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Changes
0.38.0 (*unreleased*)
======================

- fix #195: accept pathlib paths returned by Python actions as successful
string results, including ``shutil.copy`` with a Path destination.
- site/docs: landing page is now generated by sphinx (``doc/index.rst`` using ``sphinx-design``),
replacing the hand-written ``doc/index.html`` and its vendored
bootstrap / font-awesome / owl-carousel assets.
Expand Down
3 changes: 3 additions & 0 deletions doc/tasks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ For **successful** completion it must return one of:
* `None`
* a dictionary
* a string
* a ``pathlib`` path (stored as its string representation)

For **unsuccessful** completion it must return one of:

Expand Down Expand Up @@ -658,6 +659,8 @@ pathlib
`doit` supports `pathlib <https://docs.python.org/3/library/pathlib.html>`_:
file_dep, targets and CmdAction specified as a list can take as elements not
only strings but also instances of any Path class from pathlib.
Python actions may also return a pathlib path to indicate success; the result
is saved as a string, just like a returned string.

Lets take the compilation example and modify it to work with any number
of header and source files in current directory using pathlib.
Expand Down
1 change: 1 addition & 0 deletions doc/uptodate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ time *doit* was executed.
The "result" from the dependent task compared between different runs is given
by its last action.
The content for python-action is the value of the returned string or dict.
Returned ``pathlib`` paths are stored as strings.
For cmd-actions it is the output send to stdout plus stderr.

`result_dep` also supports group-tasks. In this case it will check that the
Expand Down
5 changes: 4 additions & 1 deletion doit/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ def execute(self, out=None, err=None):
pass
elif isinstance(returned_value, str):
self.result = returned_value
elif isinstance(returned_value, PurePath):
self.result = str(returned_value)
elif isinstance(returned_value, dict):
self.values = returned_value
self.result = returned_value
Expand All @@ -507,7 +509,8 @@ def execute(self, out=None, err=None):
else:
return TaskError("Python Task error: '%s'. It must return:\n"
"False for failed task.\n"
"True, None, string or dict for successful task\n"
"True, None, string, pathlib path or dict "
"for successful task\n"
"returned %s (%s)" %
(self.py_callable, returned_value,
type(returned_value)))
Expand Down
18 changes: 18 additions & 0 deletions tests/test_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,24 @@ def success_sample():
# nothing raised it was successful
my_action.execute()

def test_success_path(self):
for result in (Path('output'), PurePath('output')):
with self.subTest(result=result):
my_action = action.PythonAction(lambda: result)
self.assertIsNone(my_action.execute())
self.assertEqual(str(result), my_action.result)
self.assertIsInstance(my_action.result, str)

def test_shutil_copy_path_result(self):
with tempfile.TemporaryDirectory() as folder:
source = Path(folder) / 'input.txt'
destination = Path(folder) / 'output.txt'
source.write_text('copied content')
my_action = action.PythonAction(shutil.copy, [source, destination])
self.assertIsNone(my_action.execute())
self.assertEqual('copied content', destination.read_text())
self.assertEqual(str(destination), my_action.result)

def test_success_dict(self):
def success_sample():
return {}
Expand Down