Skip to content

Commit

Permalink
Add support for __getattr__
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew-S-Rosen committed Oct 12, 2023
1 parent d68664e commit 694d874
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 23 deletions.
24 changes: 24 additions & 0 deletions parsl/dataflow/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,33 @@ def __getitem__(self, key: Any) -> AppFuture:

return deferred_getitem_app(self, key)

def __getattr__(self, name: str) -> AppFuture:
# hack around circular imports for python_app
from parsl.app.app import python_app

# TODO: it would be nice to avoid redecorating this each time,
# which was done to avoid import loops here -- but the DFK
# is not defined at import time, and so this decoration needs
# to happen at least once per DFK. So perhaps for implementation
# simplicity, this redecoration should always happen, as happens
# for example with the globus data provider.

# TODO: this should be run on the same DFK as is executing the
# task that is associated with this future. That value isn't
# easily available here (although probably the right thing to
# do is add it to self.task_def)
deferred_getattr_app = python_app(deferred_getattr, executors=['_parsl_internal'])

return deferred_getattr_app(self, name)

# this needs python_app to be importable, but three's an import loop
# if so... so hack around it for prototyping.
# @python_app
def deferred_getitem(o: Any, k: Any) -> Any:
return o[k]

# this needs python_app to be importable, but three's an import loop
# if so... so hack around it for prototyping.
# @python_app
def deferred_getattr(o: Any, name: str) -> Any:
return getattr(o, name)
54 changes: 54 additions & 0 deletions parsl/tests/test_python_apps/test_lifted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from parsl import python_app


@python_app
def returns_a_dict():
return {"a": "X", "b": "Y"}

@python_app
def returns_a_list():
return ["X", "Y"]

@python_app
def returns_a_class():
from dataclasses import dataclass

@dataclass
class MyClass:
a: str = "X"
b: str = "Y"

return MyClass

def test_returns_a_dict():

# precondition that returns_a_dict behaves
# correctly
assert returns_a_dict().result()["a"] == "X"

# check that the deferred __getitem__ functionality works,
# allowing [] to be used on an AppFuture
assert returns_a_dict()["a"].result() == "X"

def test_returns_a_list():

# precondition that returns_a_list behaves
# correctly
assert returns_a_list().result()[0] == "X"

# check that the deferred __getitem__ functionality works,
# allowing [] to be used on an AppFuture
assert returns_a_list()[0].result() == "X"

def test_returns_a_class():

# precondition that returns_a_class behaves
# correctly
assert returns_a_class().result().a == "X"

# check that the deferred __getitem__ functionality works,
# allowing [] to be used on an AppFuture
assert returns_a_class().a.result() == "X"

# when the result is not indexable, a sensible error should
# appear in the appropriate future
23 changes: 0 additions & 23 deletions parsl/tests/test_python_apps/test_lifted_dict.py

This file was deleted.

0 comments on commit 694d874

Please sign in to comment.