Skip to content

Commit c7b2d8a

Browse files
authored
Make Globus dependencies optional in test suite (#3980)
This PR makes Globus dependencies more optional: In regular install, globus-sdk used by the very abandoned and probably untested GlobusStaging provider is now no-longer installed, which reduces the dependency footprint some. In test installs, Globus Compute tests can be picked out or skipped using `-k globus_compute`. This follows the style of `-k workqueue` and `-k taskvine` used to allow Work Queue and Taskvine to not be installed. As a consequence, the main CI workflow no longer runs the Globus Compute site tests. This PR adds a `-k globus_compute` local test to the Globus-specific workflow, again in the style of the Work Queue and Taskvine tests. The GlobusStaging uses the older "not configured implies skip implies pass in CI" model of test configuration which I find personally quite distasteful; but I have not peturbed that. Some GlobusStaging related code now imports globus_sdk later on, rather than at the top of modules, so that it will be importable even without globus_sdk. This is because we can still generate documentation without globus_sdk installed. Relevant issues: Debian bug #1116867 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1116867#30 where @emollier comments on some Globus related packaging impeding the Debian packaging of Parsl. Parsl issue #2218 # Changed Behaviour Dependencies will be loosened both in user installs and test environment installs - this might reveal interesting package incompatibilities although I am not aware of any. Import-related failures in GlobusStaging will no longer appear when importing GlobusStaging, but instead will appear when that provider tries to use globus_sdk to login/perform a transfer. ## Type of change - Code maintenance/cleanup
1 parent 77cbe4a commit c7b2d8a

File tree

10 files changed

+56
-36
lines changed

10 files changed

+56
-36
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mypy: ## run mypy checks
5151
.PHONY: gce_test
5252
gce_test: ## Run tests with GlobusComputeExecutor (--config .../globus_compute.py)
5353
pytest -v -k "not shared_fs and not issue_3620 and not staging_required" --config parsl/tests/configs/globus_compute.py parsl/tests/ --random-order --durations 10
54+
pytest -v -k "not shared_fs and not issue_3620 and not staging_required and globus_compute" --config local --random-order --durations 10
5455

5556
.PHONY: local_thread_test
5657
local_thread_test: ## run all tests with local_thread config (--config .../local_threads.py)
@@ -86,7 +87,7 @@ radical_local_test: ## Run the Radical local tests (-m radical --config local)
8687
.PHONY: config_local_test
8788
config_local_test: ## run the config-local tests (--config local)
8889
pip3 install ".[monitoring,visualization,proxystore,kubernetes]"
89-
pytest parsl/tests/ -k "not cleannet and not workqueue and not taskvine" --config local --random-order --durations 10
90+
pytest parsl/tests/ -k "not cleannet and not workqueue and not taskvine and not globus_compute" --config local --random-order --durations 10
9091

9192
.PHONY: site_test
9293
site_test: ## Run the site tests

parsl/data_provider/globus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from functools import partial
55
from typing import Optional
66

7-
import globus_sdk
87
import typeguard
98

109
import parsl
@@ -79,6 +78,7 @@ def get_authorizer(cls):
7978

8079
@classmethod
8180
def transfer_file(cls, src_ep, dst_ep, src_path, dst_path):
81+
import globus_sdk
8282
tc = globus_sdk.TransferClient(authorizer=cls.authorizer)
8383
td = globus_sdk.TransferData(tc, src_ep, dst_ep)
8484
td.add_item(src_path, dst_path)
@@ -140,6 +140,7 @@ def _update_tokens_file_on_refresh(cls, token_response):
140140
def _do_native_app_authentication(cls, client_id, redirect_uri,
141141
requested_scopes=None):
142142

143+
import globus_sdk
143144
client = globus_sdk.NativeAppAuthClient(client_id=client_id)
144145
client.oauth2_start_flow(
145146
requested_scopes=requested_scopes,
@@ -154,6 +155,7 @@ def _do_native_app_authentication(cls, client_id, redirect_uri,
154155

155156
@classmethod
156157
def _get_native_app_authorizer(cls, client_id):
158+
import globus_sdk
157159
tokens = None
158160
try:
159161
tokens = cls._load_tokens_from_file(cls.TOKEN_FILE)
Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from parsl.config import Config
22
from parsl.data_provider.data_manager import default_staging
3-
from parsl.data_provider.globus import GlobusStaging
43
from parsl.executors.threads import ThreadPoolExecutor
54

65
# If you are a developer running tests, make sure to update parsl/tests/configs/user_opts.py
@@ -10,19 +9,24 @@
109
# (i.e., user_opts['swan']['username'] -> 'your_username')
1110
from .user_opts import user_opts
1211

13-
storage_access = default_staging + [GlobusStaging(
14-
endpoint_uuid=user_opts['globus']['endpoint'],
15-
endpoint_path=user_opts['globus']['path']
16-
)]
1712

18-
config = Config(
19-
executors=[
20-
ThreadPoolExecutor(
21-
label='local_threads_globus',
22-
working_dir=user_opts['globus']['path'],
23-
storage_access=storage_access
24-
)
25-
]
26-
)
13+
def fresh_config():
14+
from parsl.data_provider.globus import GlobusStaging
15+
16+
storage_access = default_staging + [GlobusStaging(
17+
endpoint_uuid=user_opts['globus']['endpoint'],
18+
endpoint_path=user_opts['globus']['path']
19+
)]
20+
21+
return Config(
22+
executors=[
23+
ThreadPoolExecutor(
24+
label='local_threads_globus',
25+
working_dir=user_opts['globus']['path'],
26+
storage_access=storage_access
27+
)
28+
]
29+
)
30+
2731

2832
remote_writeable = user_opts['globus']['remote_writeable']

parsl/tests/test_python_apps/test_basic.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ def import_square(x):
1414
return math.pow(x, 2)
1515

1616

17-
@python_app
18-
def custom_exception():
19-
from globus_sdk import GlobusError
20-
raise GlobusError('foobar')
21-
22-
2317
def test_simple(n=2):
2418
x = double(n)
2519
assert x.result() == n * 2
@@ -38,11 +32,3 @@ def test_parallel_for(n):
3832

3933
for i in d:
4034
assert d[i].result() == 2 * i
41-
42-
43-
def test_custom_exception():
44-
from globus_sdk import GlobusError
45-
46-
x = custom_exception()
47-
with pytest.raises(GlobusError):
48-
x.result()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pytest
2+
3+
from parsl.app.app import python_app
4+
5+
6+
class CustomException(Exception):
7+
pass
8+
9+
10+
@python_app
11+
def custom_exception():
12+
from parsl.tests.test_python_apps.test_exception import CustomException
13+
raise CustomException('foobar')
14+
15+
16+
def test_custom_exception():
17+
x = custom_exception()
18+
with pytest.raises(CustomException):
19+
x.result()

parsl/tests/test_staging/test_staging_globus.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import parsl
44
from parsl.app.app import python_app
55
from parsl.data_provider.files import File
6-
from parsl.tests.configs.local_threads_globus import config, remote_writeable
6+
from parsl.tests.configs.local_threads_globus import fresh_config, remote_writeable
77

8-
local_config = config
8+
local_config = fresh_config
99

1010

1111
@python_app

parsl/tests/unit/test_globus_compute_executor.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,44 @@
22
from unittest import mock
33

44
import pytest
5-
from globus_compute_sdk import Executor
65

76
from parsl.executors import GlobusComputeExecutor
87

98

109
@pytest.fixture
1110
def mock_ex():
12-
# Not Parsl's job to test GC's Executor
11+
# Not Parsl's job to test GC's Executor, although it
12+
# still needs to be importable for these test cases.
13+
from globus_compute_sdk import Executor
14+
1315
yield mock.Mock(spec=Executor)
1416

1517

1618
@pytest.mark.local
19+
@pytest.mark.globus_compute
1720
def test_gc_executor_mock_spec(mock_ex):
1821
# a test of tests -- make sure we're using spec= in the mock
1922
with pytest.raises(AttributeError):
2023
mock_ex.aasdf()
2124

2225

2326
@pytest.mark.local
27+
@pytest.mark.globus_compute
2428
def test_gc_executor_label_default(mock_ex):
2529
gce = GlobusComputeExecutor(mock_ex)
2630
assert gce.label == type(gce).__name__, "Expect reasonable default label"
2731

2832

2933
@pytest.mark.local
34+
@pytest.mark.globus_compute
3035
def test_gc_executor_label(mock_ex, randomstring):
3136
exp_label = randomstring()
3237
gce = GlobusComputeExecutor(mock_ex, label=exp_label)
3338
assert gce.label == exp_label
3439

3540

3641
@pytest.mark.local
42+
@pytest.mark.globus_compute
3743
def test_gc_executor_resets_spec_after_submit(mock_ex, randomstring):
3844
submit_res = {randomstring(): "some submit res"}
3945
res = {"some": randomstring(), "spec": randomstring()}
@@ -57,6 +63,7 @@ def mock_submit(*a, **k):
5763

5864

5965
@pytest.mark.local
66+
@pytest.mark.globus_compute
6067
def test_gc_executor_resets_uep_after_submit(mock_ex, randomstring):
6168
uep_conf = randomstring()
6269
res = {"some": randomstring()}
@@ -79,6 +86,7 @@ def mock_submit(*a, **k):
7986

8087

8188
@pytest.mark.local
89+
@pytest.mark.globus_compute
8290
def test_gc_executor_happy_path(mock_ex, randomstring):
8391
mock_fn = mock.Mock()
8492
args = tuple(randomstring() for _ in range(random.randint(0, 3)))
@@ -95,6 +103,7 @@ def test_gc_executor_happy_path(mock_ex, randomstring):
95103

96104

97105
@pytest.mark.local
106+
@pytest.mark.globus_compute
98107
def test_gc_executor_shuts_down_asynchronously(mock_ex):
99108
gce = GlobusComputeExecutor(mock_ex)
100109
gce.shutdown()

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ typeguard>=2.10,!=3.*,<5
88

99

1010
typing-extensions>=4.6,<5
11-
globus-sdk
1211
dill
1312
tblib
1413
requests

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
'proxystore': ['proxystore'],
4747
'radical-pilot': ['radical.pilot==1.90', 'radical.utils==1.90'],
4848
'globus_compute': ['globus_compute_sdk>=2.34.0'],
49+
'globus_transfer': ['globus-sdk'],
4950
# Disabling psi-j since github direct links are not allowed by pypi
5051
# 'psij': ['psi-j-parsl@git+https://github.com/ExaWorks/psi-j-parsl']
5152
}

test-requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ types-mock
1010
types-python-dateutil
1111
types-requests
1212
mpi4py
13-
globus-compute-sdk>=2.34.0
1413

1514
# sqlalchemy is needed for typechecking, so it's here
1615
# as well as at runtime for optional monitoring execution

0 commit comments

Comments
 (0)