-
Notifications
You must be signed in to change notification settings - Fork 202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove empty default values for inputs/outputs arguments #3744
base: master
Are you sure you want to change the base?
Remove empty default values for inputs/outputs arguments #3744
Conversation
looks like something breaks in the work queue executor here - i'll have a poke at that |
@@ -301,7 +301,7 @@ New Functionality | |||
.. code-block:: python | |||
|
|||
@bash_app | |||
def cat(inputs=(), outputs=()): | |||
def cat(inputs, outputs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not confident, though, that Parsl treats args
and kwargs
the same with respect to these two special arguments. I'm going to dig a bit more and see if I can convince myself that these do not have to be keyword arguments in order to be recognized correctly, but I'm worried that the previous =()
default argument habit might have been related to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I expect that's where that style came from too - but it's not particularly nice and it would be good to figure it out and fix rather than behave not-like-normal-Python
def reduce_app(inputs): | ||
return sum(inputs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's one of the cases where an empty iterable would actually work:
>>> inputs = tuple()
>>> sum(inputs)
0
But I'm not sure that's useful behavior for this app.
parsl/data_provider/ftp.py
Outdated
@@ -66,7 +66,7 @@ def wrapper(*args, **kwargs): | |||
return wrapper | |||
|
|||
|
|||
def _ftp_stage_in(working_dir, parent_fut=None, outputs=[], _parsl_staging_inhibit=True): | |||
def _ftp_stage_in(working_dir, outputs, parent_fut=None, _parsl_staging_inhibit=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguments with default values must follow those without.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can force args to be keyword args with *
like this:
def a(x,y,*, z=10,q):
I've personally been encouraging the use of *
in the codebase because it provides a cleaner separation between positional and keyword args, and forces people to use the keyword names, which makes long argument lists less bug-prone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the call sites for the functions where I'd reordered the arguments all provide kwargs anyway, so I updated the arg lists to split args/kwargs with *
and reverted the argument order.
@@ -788,7 +788,7 @@ def _add_input_deps(self, executor: str, args: Sequence[Any], kwargs: Dict[str, | |||
logger.debug("Not performing input staging") | |||
return args, kwargs, func | |||
|
|||
inputs = kwargs.get('inputs', []) | |||
inputs = kwargs.get('inputs') or [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and in several places below, the old code would assign None
to inputs
if the app's formal arguments contained inputs=None
, and then the enumerate()
below would fail. The new code will assign []
to inputs
in this case.
@@ -944,7 +944,7 @@ def append_failure(e: Exception, dep: Future) -> None: | |||
append_failure(e, dep) | |||
|
|||
# Check for futures in inputs=[<fut>...] | |||
if 'inputs' in kwargs: | |||
if kwargs.get('inputs'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to above: If inputs=None
is supplied, 'inputs' in kwargs
will be True
, but None
will not be iterable below. Now, inputs
has to be specified and be truthy (which None
is not). Other bad but truthy values could still make it past this check, but that was previously the case, too.
@@ -12,7 +12,7 @@ TARBALL="cctools-$CCTOOLS_VERSION-x86_64-ubuntu20.04.tar.gz" | |||
|
|||
# If stderr is *not* a TTY, then disable progress bar and show HTTP response headers | |||
[[ ! -t 1 ]] && NO_VERBOSE="--no-verbose" SHOW_HEADERS="-S" | |||
wget "$NO_VERBOSE" "$SHOW_HEADERS" -O /tmp/cctools.tar.gz "https://github.com/cooperative-computing-lab/cctools/releases/download/release/$CCTOOLS_VERSION/$TARBALL" | |||
wget $NO_VERBOSE $SHOW_HEADERS -O /tmp/cctools.tar.gz "https://github.com/cooperative-computing-lab/cctools/releases/download/release/$CCTOOLS_VERSION/$TARBALL" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wget
, at least on my system, fails when NO_VERBOSE
and SHOW_HEADERS
are empty/unset, as the command like ends up looking like
wget '' '' -O /tmp/cctools.tar.gz "<the url>"
and e.g.
~ $ wget '' '' -O /dev/null https://google.com &>/dev/null; echo $?
1
~ $ wget -O /dev/null https://google.com &>/dev/null; echo $?
0
Due to the set -e
above, the script then aborted. This change fixed it for me.
@@ -28,7 +28,7 @@ def test_immediate_datafuture(): | |||
""" | |||
|
|||
import time | |||
fu = echo_slow_message("Hello world", sleep=1, outputs=["hello.1.txt"]) | |||
fu = echo_slow_message("Hello world", outputs=["hello.1.txt"], sleep=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reordered these to match the reordered echo_slow_message()
argument list, above, though it wasn't technically necessary since they're kwargs.
A failing test calls this bash_app, which it updated in this PR to replace It might be worth re-running the tests without the |
The error I mentioned above might be a byproduct, not a root cause. Locally I'm seeing errors about importing the Also, I found that my local hang for the I'm happy to help with anything, but may lack context to understand some failures. I'll put this on my back burner for now. I'd understand if you decided that this isn't worth pursuing at the moment, in which case it might make sense to roll back the |
I think this is worth pursuing and probably the right thing next is for me to figure out why things are failing on this PR. The changes here are a reasonable target for how things "should" work. |
Description
Using a mutable object like
[]
as a default argument value can be dangerous (see the description of this PR for the tutorial, the Important warning from the Python docs, Google's style guide, or the sad stories from googling "python mutable default argument"). This PR removes those.An immutable default-argument value is nominally safe, but erroneous in a case like that where
inputs=()
appears in the formal arguments butinputs[0]
appears in the function body (i.e.,()
cannot possibly be a valid value). This PR removes those.There were a handful of cases, like
inputs=()
followed byfor input in inputs: ...
, where an empty iterable would be safe and correct, but where it seemed like it wouldn't make sense to call an app this way. This PR removes those, but I'd be happy to restore those (or anything, really) if I misunderstood the intent.Given that the common "pythonic" advice is to specify e.g.
inputs=None
, I updated some bits of code to deal with this correctly. I'll flag those via inline comments.I was able to pass the
make test
tests except forhtex_local_alternate_test
,wqex_local_test
,vineex_local_test
,radical_local_test
, andconfig_local_test
.htex_local_alternate_test
andconfig_local_test
run but appear to hang,wqex_local_test
andvineex_local_test
appear to require Python modules I don't have installed, andradical_local_test
fails with a complaint aboutmpiexec
-- but the behavior is the same for me onmaster
, so I'm not sure it has anything to do with the PR changes.Changed Behaviour
I wouldn't expect existing workflows to behave differently.
Fixes
NA but I'd be happy to open an Issue if it'd be helpful.
Type of change
Choose which options apply, and delete the ones which do not apply.